Появилась задача хранить документы с разной структурой в одной коллекции.
Вопрос на СО
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().ToArray();
Или всех кошек (без кошачьих особенностей)
var query = Query.EQ("_t", "Cat"); var cats = collection.FindAs(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 Eyes { get; set; } } public class Dog : Animal { public String _Dog { get; set; } public List Eyes { get; set; } }
Изменения при добавлении документов в коллекцию.
collection.Insert(new Cat { _Animal = "meow", _Cat = "murka", Eyes = new List() { new CatEye(){Color="green", Size="large"}, new CatEye(){Color="blue", Size="middle"} } }); collection.Insert(new Cat { _Animal = "murr", _Cat = "zoyka", Eyes = new List() { new CatEye(){Color="red", Size="small"}, new CatEye(){Color="pink", Size="middle"} } }); collection.Insert(new Dog { _Animal = "bobik", _Dog = "gav gav", Eyes = new List() { new DogEye(){Color="red", Shape="round"}, new DogEye(){Color="pink", Shape="square"} } });
Выборка (как и в предыдущем варианте)
var query = Query.EQ("_t", "Cat"); var cats = collection.FindAs(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 Eyes { get; set; } }
Он на самом деле трехглазый. Добавим его в коллекцию.
collection.Insert(new CatDog { _Animal = "kotopes", _CatDog = "three eyed", Eyes = new List() { 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(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" } ] }
Итого
Можно работать с коллекциями объектов, производных от одного класса. Это хорошо.
Названия типов хранятся в базе, и в случае рефакторинга… Ну вы поняли.
Может я где-то что-то упустила или недопоняла, не судите строго.