使用C#MongoDB LINQ和鉴别器

时间:2012-07-22 00:58:47

标签: c# linq mongodb discriminator

我有一个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我的Where子句(特定于C类元素)时,它似乎被应用于A,B和C的文档。

我尝试添加.OfType(Of C)但没有效果,尝试首先使用.ToList转换为列表,但我仍然遇到同样的错误。有任何想法吗?

作为背景,我的客户端代码通常会处理类型为D的对象.A,B,C共享从D继承的许多常见属性,我想将索引放在上面,因此我将它们放在一个集合中。但是,偶尔我需要在特殊情况下直接引用类型A,B或C的对象。

5 个答案:

答案 0 :(得分:12)

使用多态类型层次结构时,集合变量和LINQ查询应该从基类开始。例如,要从数据库中读取所有类型A的文档,您将编写:

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&lt; A&gt; (不是MongoQueryable&lt; D&gt;)因为OfType()调用改变了IQueryable的类型。

答案 1 :(得分:2)

使用.AsQueryable<D>().OfType<C>.这应该有效地自动包含鉴别器。问题是我们不一定知道您对A,B和C使用相同的集合,因此不知道当您执行AsQueryable<C>()时,我们实际上需要添加一个鉴别器。我们将研究如何在未来使其更加无缝。

答案 2 :(得分:1)

就我而言,我需要能够检索A,B以及基类D.所有这些都存储在同一个集合中。这就是我最终在我的存储库中所做的事情:

    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

这有效地将基类和子类添加到鉴别器中。

_t: D, A

然后允许我查询这两种类型。

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

答案 3 :(得分:0)

我遇到了同样的问题:.AsQueryable()和OfType()都不会在查询中产生“_t”鉴别器。特别是当你有一个高级通用的OpenQuery()方法时,你可以将IQueryable接口暴露给任何插件,而不需要Mongo驱动程序库引用。无论如何,如果使用静态类来访问你的集合,告诉BsonClassMap特定的类是root将解决这个问题。

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

答案 4 :(得分:0)

我使用的是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
  }
}