Using C # MongoDB LINQ with discriminator

I have one MongoDB collection that stores documents of three different classes (A, B, C), which all inherit from the common class D.

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

If I issue a LINQ query, for example the following (using VB):

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

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

Shouldn't the discriminator kick in the AsQueryable(Of C) code here? It seems that when I .Count Where clause, which refers to class C elements, applies to documents A, B and C.

I tried adding .OfType(Of C) without effect, tried to convert to a list first with .ToList , but I keep getting the same error. Any ideas?

As a background, my client code usually deals with objects like D. A, B, C share a lot of common properties inherited from D, on which I want to put indexes, so I put them in one collection. However, sometimes I need to refer directly to an object of type A, B, or C in special circumstances.

+4
source share
5 answers

When working with a polymorphic type hierarchy, your collection variable and your LINQ queries must start with the base class. For example, to read all documents of type A back from the database you must 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 MongoDB native query using:

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

Note that you need to send a query to MongoQueryable <A> (not MongoQueryable <D>), because calling OfType () changed the type of IQueryable.

+9
source

Use .AsQueryable<D>().OfType<C>. This should effectively enable the discriminator automatically. The problem is that we don’t necessarily know that you are using the same collection for A, B and C and therefore do not know that when you make AsQueryable<C>() , we really need to add the discriminator. We will look at how to make it smoother in the future.

+2
source

In my case, I needed to get A, B, and also D, the base class. All of them were saved in one collection. This is what I did 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 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 and B as the base class
+1
source

I ran into the same problem: neither the .AsQueryable () method nor OfType () would call the "_t" discriminator in the request. Especially if you have a general high-level OpenQuery () method so that you can open the IQueryable interface for any plugins without requiring a link to the Mongo driver library. In any case, if you use a static class to access your collections, telling BsonClassMap that the particular class is root, this problem will be solved.

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

I am using the C # driver v.1.9.2, but it looks like it was introduced in version 1.1.1.

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

The profile contains the following:

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

All Articles