Использование C# MongoDB LINQ с дискриминатором
У меня есть одна коллекция MongoDB, содержащая документы трех разных классов (A,B,C), которые все наследуются от общего класса D.
Используя официальный драйвер C#, я вставил документы всех трех типов (A,B,C), и все они отображаются правильно с дискриминатором _t, и в моем коде их карты классов зарегистрированы.
Если я выдаю запрос LINQ, например, следующий (используя VB):
dim Result = database.GetCollection("mycol").AsQueryable(Of C).Where(some where clause)
Если я посчитаю результаты этого, я получаю сообщение об ошибке "Элемент" имя элемента из класса A "не соответствует ни одному полю или свойству класса C."
Разве дискриминатор не должен пнуть здесь, в AsQueryable(Of C)
код? Похоже, что когда я выпускаю .Count
Предложение my, относящееся к элементам класса C, применяется к документам A, B и C.
Я пробовал добавлять .OfType(Of C)
безрезультатно, попытался сначала преобразовать в список с .ToList
, но я продолжаю получать ту же ошибку. Есть идеи?
В качестве фона мой клиентский код обычно будет работать с объектами типа D. A, B, C имеют много общих свойств, унаследованных от D, на которые я хочу наложить индексы, поэтому я поместил их в одну коллекцию. Однако иногда мне нужно напрямую ссылаться на объект типов A, B или C в особых обстоятельствах.
5 ответов
При работе с полиморфной иерархией типов ваша переменная коллекции и ваши запросы LINQ должны начинаться с точки зрения базового класса. Например, чтобы прочитать все документы типа А из базы данных, вы должны написать:
var collection = database.GetCollection<D>("mycol");
var query = collection.AsQueryable<D>().OfType<A>();
foreach (var a in query)
{
// process document of type A
}
В целях диагностики вы можете увидеть соответствующий собственный запрос MongoDB, используя:
var json = ((MongoQueryable<A>)query).GetMongoQuery().ToJson();
Обратите внимание, что вы должны привести запрос к MongoQueryable (не MongoQueryable
Использование .AsQueryable<D>().OfType<C>.
Это должно эффективно включать дискриминатор автоматически. Проблема в том, что мы не обязательно знаем, что вы используете одну и ту же коллекцию для A, B и C, и поэтому не знаем, когда вы это делаете AsQueryable<C>()
нам на самом деле нужно добавить дискриминатор. Мы рассмотрим, как сделать это более плавным в будущем.
Я столкнулся с той же проблемой: ни.AsQueryable(), ни OfType() не произведут дискриминатор "_t" в запросе. Особенно, когда у вас есть высокоуровневый универсальный метод OpenQuery(), так что вы можете предоставить интерфейс IQueryable любым плагинам, не требуя ссылки на библиотеку драйверов Mongo. В любом случае, если для доступа к вашим коллекциям используется статический класс, сообщение BsonClassMap о том, что конкретный класс является корневым, решит эту проблему.
// Data Repository class
public static class MyDataRepositoryMethods
{
static MyDataRepositoryMethods()
{
BsonClassMap.RegisterClassMap<MyBaseClassOfStuff>(x =>
{
x.AutoMap();
x.SetIsRootClass(true);
});
}
/// <summary>
/// Returns a queryable data sample collection
/// </summary>
/// <typeparam name="TMyBaseClassOfStuff"></typeparam>
/// <returns></returns>
public static IQueryable<TMyBaseClassOfStuff> OpenQuery<TMyBaseClassOfStuff>() where TMyBaseClassOfStuff: MyBaseClassOfStuff
{
using (var storage = new MongoDataStorageStuff())
{
// _t discriminator will reflect the final class, not the base class (unless you pass generic type info of the base class
var dataItems = storage.GetCollection<TMyBaseClassOfStuff>("abcdef").OfType<TMyBaseClassOfStuff>();
return dataItems ;
}
}
}
В моем случае мне нужно было получить A, B, а также D, базовый класс. Все они были сохранены в одной коллекции. Это то, что я закончил делать в своем хранилище:
Shared Sub New () BsonClassMap.RegisterClassMap (Of D) (Sub (е) f.AutoMap () f.SetIsRootClass (True) End Sub) BsonClassMap.RegisterClassMap (Of A) () BsonClassMap.RegisterClassMap (Of B) () End Sub
Это эффективно добавляет и базовый класс и подкласс к дискриминатору.
_t: D, A
Который затем позволяет мне запрашивать оба типа.
Dim collection = _db.GetCollection (of D) ("Предметы") Dim resultA = collection.AsQueryable (). OfType (of A) 'Только тип A Dim resultB = collection.AsQueryable().OfType(of B) 'Только тип B Dim resultD = collection.AsQueryable().OfType(of D) 'и A, и B в качестве базового класса
Я использую C# драйвер v1.9.2, но, кажется, это было введено еще в v1.4.1.
collection = MongoDatabase.GetCollection<MyBaseClass>("MyCollectionName");
long count = collection.AsQueryable().OfType<MyDerivedClass>().Count();
Профиль содержит это:
command: {
"count" : "MyCollectionName",
"query" : {
"_t" : "D" // MyDerivedClass discriminator value
}
}