C#-MongoDB Polymorphic Classes

Появилась задача хранить документы с разной структурой в одной коллекции.

Вопрос на СО
http://stackoverflow.com/questions/13956413/mongodb-embedded-polymorphic-objects

Документация
http://docs.mongodb.org/ecosystem/tutorial/serialize-documents-with-the-csharp-driver/#polymorphic-classes-and-discriminators

Мои примеры.

Основная функция и подключение к бд

class Program
    {
        static void Main(string[] args)
        {
            var mongoClient = new MongoClient("mongodb://localhost");
            var mongoServer = mongoClient.GetServer();
 
            var collection = mongoServer.GetDatabase("test").GetCollection("animals");
            collection.Drop(); //для чистоты эксперимента
 
            //Здесь добавим документы в коллекцию
 
            //Здесь сделаем выборки
         }
      }

Классы. Подробнее об атрибутах написано в документации. Кстати, у меня не получилось дать полям названия, совпадающие с названием класса.
В принципе, вставить разные документы в коллекцию можно и без всяких атрибутов. Они понадобятся, чтобы подготовить коллекцию к чтению из программы.

    [BsonDiscriminator(RootClass = true)]
    [BsonKnownTypes(typeof(Cat), typeof(Dog))]
    public class Animal
    {
        public ObjectId Id { get; set; }
        public String _Animal { get; set; }
    }
 
    [BsonKnownTypes(typeof(Lion), typeof(Tiger))]
    public class Cat : Animal
    {
        public String _Cat { get; set; }
    }
 
    public class Dog : Animal
    {
        public String _Dog { get; set; }
    }
 
    public class Lion : Cat
    {
        public String _Lion { get; set; }
    }
 
    public class Tiger : Cat
    {
        public String _Tiger { get; set; }
    }

Добавляем документы в коллекцию

            collection.Insert(new Cat
            {
                _Animal = "meow",
                _Cat = "murka"
            });
            collection.Insert(new Cat
            {
                _Animal = "murr",
                _Cat = "zoyka"
            });
            collection.Insert(new Animal
            {
                _Animal = "omnomnom",
            });
            collection.Insert(new Tiger
            {
                _Animal = "rrr",
                _Cat = "kitty",
                _Tiger = "poloskin"
            });
            collection.Insert(new Dog
            {
                _Animal = "bobik",
                _Dog = "gav gav"
            });
            collection.Insert(new Lion
            {
                _Animal = "grr",
                _Cat = "big",
                _Lion = "king"
            });

Выбрать всех животных

            var p = collection.AsQueryable<Animal>().ToArray();

Или всех кошек (без кошачьих особенностей)

            var query = Query.EQ("_t", "Cat");
            var cats = collection.FindAs<Animal>(query).ToArray();

При этом коллекция имеет вид

{
        "_id" : ObjectId("51ee525dfafee225483379cd"),
        "_t" : [
                "Animal",
                "Cat"
        ],
        "_Animal" : "meow",
        "_Cat" : "murka"
}
{
        "_id" : ObjectId("51ee525dfafee225483379ce"),
        "_t" : [
                "Animal",
                "Cat"
        ],
        "_Animal" : "murr",
        "_Cat" : "zoyka"
}
{
        "_id" : ObjectId("51ee525dfafee225483379cf"),
        "_t" : "Animal",
        "_Animal" : "omnomnom"
}
{
        "_id" : ObjectId("51ee525dfafee225483379d0"),
        "_t" : [
                "Animal",
                "Cat",
                "Tiger"
        ],
        "_Animal" : "rrr",
        "_Cat" : "kitty",
        "_Tiger" : "poloskin"
}
{
        "_id" : ObjectId("51ee525dfafee225483379d1"),
        "_t" : [
                "Animal",
                "Dog"
        ],
        "_Animal" : "bobik",
        "_Dog" : "gav gav"
}
{
        "_id" : ObjectId("51ee525dfafee225483379d2"),
        "_t" : [
                "Animal",
                "Cat",
                "Lion"
        ],
        "_Animal" : "grr",
        "_Cat" : "big",
        "_Lion" : "king"
}

Пример с вложенными коллекциями.

Добавим кошкам и собакам глаза.

    public class Eye
    {
        public String Color { get; set; }
    }
 
    public class CatEye : Eye
    {
        public String Size { get; set; }
    }
 
    public class DogEye : Eye
    {
        public String Shape { get; set; }
    }

Изменения в классах кошки и собаки.

    public class Cat : Animal
    {
        public String _Cat { get; set; }
        public List<CatEye> Eyes { get; set; }
    }
 
    public class Dog : Animal
    {
        public String _Dog { get; set; }
        public List<DogEye> Eyes { get; set; }
    }

Изменения при добавлении документов в коллекцию.

            collection.Insert(new Cat
            {
                _Animal = "meow",
                _Cat = "murka",
                Eyes = new List<CatEye>()
                {
                    new CatEye(){Color="green", Size="large"},
                    new CatEye(){Color="blue", Size="middle"}
                }
            });
            collection.Insert(new Cat
            {
                _Animal = "murr",
                _Cat = "zoyka",
                Eyes = new List<CatEye>()
                {
                    new CatEye(){Color="red", Size="small"},
                    new CatEye(){Color="pink", Size="middle"}
                }
            });
            collection.Insert(new Dog
            {
                _Animal = "bobik",
                _Dog = "gav gav",
                Eyes = new List<DogEye>()
                {
                    new DogEye(){Color="red", Shape="round"},
                    new DogEye(){Color="pink", Shape="square"}
                }
            });

Выборка (как и в предыдущем варианте)

            var query = Query.EQ("_t", "Cat");
            var cats = collection.FindAs<Animal>(query).ToArray();

Коллекция

{
        "_id" : ObjectId("51ee622cfafee21464fd1fb2"),
        "_t" : [
                "Animal",
                "Cat"
        ],
        "_Animal" : "meow",
        "_Cat" : "murka",
        "Eyes" : [
                {
                        "Color" : "green",
                        "Size" : "large"
                },
                {
                        "Color" : "blue",
                        "Size" : "middle"
                }
        ]
}
{
        "_id" : ObjectId("51ee622cfafee21464fd1fb3"),
        "_t" : [
                "Animal",
                "Cat"
        ],
        "_Animal" : "murr",
        "_Cat" : "zoyka",
        "Eyes" : [
                {
                        "Color" : "red",
                        "Size" : "small"
                },
                {
                        "Color" : "pink",
                        "Size" : "middle"
                }
        ]
}
{
        "_id" : ObjectId("51ee622cfafee21464fd1fb4"),
        "_t" : [
                "Animal",
                "Dog"
        ],
        "_Animal" : "bobik",
        "_Dog" : "gav gav",
        "Eyes" : [
                {
                        "Color" : "red",
                        "Shape" : "round"
                },
                {
                        "Color" : "pink",
                        "Shape" : "square"
                }
        ]
}

Пример с разными потомками в одной коллекции (такой же по ссылке на SO).

Допустим, у нас есть котопёс, унаследованный от животного.

    public class CatDog : Animal
    {
        public String _CatDog { get; set; }
        public List<Eye> Eyes { get; set; }
    }

Он на самом деле трехглазый. Добавим его в коллекцию.

            collection.Insert(new CatDog
            {
                _Animal = "kotopes",
                _CatDog = "three eyed",
                Eyes = new List<Eye>()
                {
                    new Eye(){Color="transparent"},
                    new CatEye(){Color="red", Size ="big"},
                    new DogEye(){Color="pink", Shape="square"}
                }
            });

И посмотрим ему в глаза.

            var query = Query.EQ("_Animal", "kotopes");
            var eyes = collection.FindOneAs<CatDog>(query).Eyes;
 
            foreach (var eye in eyes)
            {
                Console.WriteLine(eye.GetType().ToString());
            }

Результат:
PolyMongo.Eye
PolyMongo.CatEye
PolyMongo.DogEye

Документ в коллекции:

{
        "_id" : ObjectId("51ee648afafee2248811b94e"),
        "_t" : [
                "Animal",
                "CatDog"
        ],
        "_Animal" : "kotopes",
        "_CatDog" : "three eyed",
        "Eyes" : [
                {
                        "Color" : "transparent"
                },
                {
                        "_t" : "CatEye",
                        "Color" : "red",
                        "Size" : "big"
                },
                {
                        "_t" : "DogEye",
                        "Color" : "pink",
                        "Shape" : "square"
                }
        ]
}

Итого
Можно работать с коллекциями объектов, производных от одного класса. Это хорошо.
Названия типов хранятся в базе, и в случае рефакторинга… Ну вы поняли.

Может я где-то что-то упустила или недопоняла, не судите строго.

Оставить комментарий


Примечание - Вы можете использовать эти HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>