使用C# MongoDB LINQ和鉴别器

7

我有一个单一的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转换为List,但仍然出现相同的错误。有什么想法吗?
背景:我的客户端代码通常处理D类型的对象。A、B、C共享许多从D继承的公共属性,我想在其上建立索引,因此将它们放在一个单独的集合中。然而,在特殊情况下,我偶尔需要直接引用A、B或C类型的对象。
5个回答

18

当使用多态类型层次结构时,您的集合变量和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<A>(而不是MongoQueryable<D>),因为OfType()调用更改了IQueryable的类型。


2
非常有价值的信息。我鼓励您将其添加到mongodb文档中的多态性部分(如果已经存在,请进行指向)-- 我知道我可以用得上它。 - Jeremy Smith

3

使用.AsQueryable<D>().OfType<C>。这样可以自动包含鉴别器。问题在于我们不一定知道您是否在使用相同的集合来处理A、B和C,因此当您执行AsQueryable<C>()时,我们实际上需要添加一个鉴别器。我们将会研究如何使这更加无缝。


嗨,Craig,非常感谢您的快速回复。不幸的是,没有任何不同。如我在原始帖子中所提到的,我已经尝试过了,但似乎并没有改变任何东西。驱动程序是否附带任何诊断工具?例如,是否可以查看基础查询?就像您可以使用Query对象(即Query.ToString)一样。 - Greg May
我也尝试过使用 AsQueryable().OfType(Of C) 而不是 AsQueryable(Of C).OfType(Of C),但是出现了"No coercion operator is defined between types 'MongoDB.Bson.BsonDocument' and 'C'"的错误提示。 - Greg May
再多说一句 :) 我询问诊断的原因是,我可以使用 IMongoQuery 发出几乎相同的命令,并获得正确的结果。我正在尝试使用泛型为我的非 MongoDB 感知类组合一个通用查询方法,因此 LINQ 很适合。 - Greg May
是的,抱歉...看起来格式化破坏了我的答案中的大于和小于符号(我应该检查一下)。Robert的答案格式更好。 - Craig Wilson
嗨,Craig,谢谢你 - 你和Robert修改后的答案让我重新回到了正轨。 - Greg May

1
我遇到了同样的问题:无论是.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 ;
        }
    }
}

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) ' 只有类型A Dim resultB = collection.AsQueryable().OfType(of B) ' 只有类型B Dim resultD = collection.AsQueryable().OfType(of D) ' 基类A和B都有

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接