Появилась задача хранить документы с разной структурой в одной коллекции.
Вопрос на СО
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"
}
]
}
Итого
Можно работать с коллекциями объектов, производных от одного класса. Это хорошо.
Названия типов хранятся в базе, и в случае рефакторинга… Ну вы поняли.
Может я где-то что-то упустила или недопоняла, не судите строго.