Using C# MongoDB LINQ with discriminator

谁说胖子不能爱 提交于 2019-12-12 10:43:17

问题


I have a single MongoDB collection holding documents of three different classes (A,B,C) which all inherit from a common class D.

Using the official C# driver, I have inserted documents of all three types (A,B,C) and they all appear correctly with the _t discriminator, and in my code their class maps are registered.

If I issue a LINQ query such as the following (using VB):

dim Result = database.GetCollection("mycol").AsQueryable(Of C).Where(some where clause)

If I count the results of this, I am getting an error "Element 'an element name from class A' does not match any field or property of class C."

Isn't the discriminator meant to kick in here in the AsQueryable(Of C) code? It appears that when I issue .Count my Where clause, which is specific to elements of class C, is being applied to documents of A,B, and C.

I have tried adding .OfType(Of C) with no effect, have tried converting to a List first with .ToList, but I continue to get the same error. Any ideas?

As background, my client code will usually deal with objects of type D. A, B, C share a lot of common properties inherited from D which I want to put indexes on, hence I put them in a single collection. However, occassionally I need to directly reference an object of types A, B, or C in special circumstances.


回答1:


When working with a polymorphic type hierarchy your collection variable and your LINQ queries should start in terms of the base class. For example, to read all the documents of type A back from the database you would write:

var collection = database.GetCollection<D>("mycol");
var query = collection.AsQueryable<D>().OfType<A>();
foreach (var a in query)
{
    // process document of type A
}

For diagnostic purposes you can see the corresponding native MongoDB query using:

var json = ((MongoQueryable<A>)query).GetMongoQuery().ToJson();

Note that you have to cast query to a MongoQueryable<A> (not MongoQueryable<D>) because the OfType() call changed the type of the IQueryable.




回答2:


Use .AsQueryable<D>().OfType<C>. This should effectively include the discriminator automatically. The issue is that we don't necessarily know that you are using the same collection for A, B, and C and therefore don't know that when you do AsQueryable<C>(), we actually need to add a discriminator. We'll be looking into how to make this more seamless in the future.




回答3:


In my case, I needed to be able to retrieve A, B, as well as D, the base class. All of these were stored in the same collection. This is what I ended up doing in my repository:

    Shared Sub New()
        BsonClassMap.RegisterClassMap(Of D)(
            Sub(f)
                f.AutoMap()
                f.SetIsRootClass(True)
            End Sub)

        BsonClassMap.RegisterClassMap(Of A)()
        BsonClassMap.RegisterClassMap(Of B)()

    End Sub

This effectively adds the both the base class and the subclass to the discriminator .

_t: D, A

Which then allows me to query both types.

Dim collection = _db.GetCollection(of D)("Items")

Dim resultA = collection.AsQueryable().OfType(of A)  ' Only type A's
Dim resultB = collection.AsQueryable().OfType(of B)  ' Only type B's
Dim resultD = collection.AsQueryable().OfType(of D)  ' Both A's and B's as the base class



回答4:


I have ran into the same issue: neither .AsQueryable() nor OfType() would produce a "_t" discriminator in the query. Especially when you have a high-level generic OpenQuery() method so that you can expose IQueryable interface to any plugins without them requiring Mongo driver library reference. Anyway, if using a static class for accessing your collections, telling BsonClassMap that the specific class is root will resolve this problem.

// 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 ;
        }
    }
}



回答5:


I'm using C# driver v1.9.2 but seems this was introduced back in v1.4.1.

collection = MongoDatabase.GetCollection<MyBaseClass>("MyCollectionName");
long count = collection.AsQueryable().OfType<MyDerivedClass>().Count();

Profile contains this:

command: {
  "count" : "MyCollectionName",
  "query" : {
    "_t" : "D"  // MyDerivedClass discriminator value
  }
}


来源:https://stackoverflow.com/questions/11597005/using-c-sharp-mongodb-linq-with-discriminator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!