Использование 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), потому что вызов OfType() изменил тип IQueryable.

Использование .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
  }
}
Другие вопросы по тегам