【问题标题】:Querying derived type values using MongoDB C# driver使用 MongoDB C# 驱动程序查询派生类型值
【发布时间】:2017-03-03 18:08:34
【问题描述】:

我正在从旧的 MongoDB 驱动程序中移植一些代码以使用新的驱动程序,但遇到了问题。我有一个集合,其中包含来自公共基类的多个派生类型。以前,我能够使用派生类属性查询集合(使用基类型声明)并检索派生类文档。所以给定这些类:

[BsonDiscriminator(RootClass = true)]
[BsonKnownTypes(typeof(Cat),typeof(Dog))]
class Animal
{
    [BsonId(IdGenerator = typeof(StringObjectIdGenerator))]
    public string Id { get; set; }

    public string Name { get; set; }
}

class Cat : Animal
{
    public bool LikesFish { get; set; }
}

class Dog : Animal
{
    public string FavouriteBone { get; set; }
}

然后我可以做类似的事情:

MongoCollection<Animal> animals = db.GetCollection<Animal>("Animals");
var q = Query<Cat>.EQ(c => c.LikesFish, true);
var catsThatLikeFish = animals.FindAs<Animal>(q).ToList();

效果很好。

但是现在我必须输入过滤器并且不能再编译:

IMongoCollection<Animal> animals = db.GetCollection<Animal>("Animals");
var query = Builders<Cat>.Filter.Eq(c => c.LikesFish, true);
var catsThatLikeFish = animals.FindSync(query);

并得到这个错误:

Error CS0411 The type arguments for method 'IMongoCollection<Animal>.FindSync<TProjection>(FilterDefinition<Animal>, FindOptions<Animal, TProjection>, CancellationToken)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

使用新驱动程序是否不再可能?我们有允许对这个集合进行通用查询的类,我现在看不到任何优雅的方法。

编辑:

可悲的是,单独的集合是行不通的,因为我们混合过滤器表达式以使用相同的查询拉回不同的类型。在上面的“猫和狗”示例中,如下所示:

var catQuery = Query<Cat>.EQ(c => c.LikesFish, true);
var dogQuery = Query<Dog>.EQ(c => c.FavouriteBone, "Beef");
var q = Query.Or(catQuery, dogQuery);

var catsThatLikeFishOrDogsThatLikeBeef = animals.FindAs<Animal>(q).ToList();

我会看看上面的“nameof”方法——它可能有效,但对我来说似乎缺乏旧方法的优雅......

非常感谢任何帮助!

谢谢,

史蒂夫

【问题讨论】:

  • 除了下面 Maksim 的回答之外,在新的 API 中还有一个 OfType() 方法... IMongoCollection dogs = db.GetCollection("Animals").OfType().

标签: c# .net mongodb mongodb-.net-driver


【解决方案1】:

您有一个猫和狗的集合,并且您只想过滤猫?或者你想要所有的狗,只有那些喜欢鱼的猫?

在第一种情况下,我建议查询集合中的猫或狗:

var collectionAsCats = db.GetCollection<Cat>("Animal");
var collectionAsDogs = db.GetCollection<Dog>("Animal");

var catsDoesntlikeFish = collectionAsCats.Find(c => c.LikesFish == false).ToList();
var dogs = collectionAsDogs.Find(c => c.FavouriteBone == "bla").ToList();

或者您可以拥有一组 Animals 并使用字符串查询您的数据:

var collectionAll = db.GetCollection<Animal>("Animal");
var filter = Builders<Animal>.Filter.Eq(nameof(Cat.LikesFish), false);
var catsDoesntlikeFish = collectionAll.Find(filter).As<Animal>().ToList();

如果您想让狗与猫在一起,您可以扩展此过滤器:

var collectionAll = db.GetCollection<Animal>("Animal");
var filter = Builders<Animal>.Filter.Eq(nameof(Cat.LikesFish), false);
var exists = Builders<Animal>.Filter.Exists(nameof(Cat.LikesFish), false);
var orFilter = Builders<Animal>.Filter.Or(filter, exists);
var catsDoesntlikeFishAndDogs = collectionAll.Find(orFilter).ToList();

编辑

我在这里添加 Craig Wilson 的评论,非常感兴趣的信息(感谢 Craig):

在新的 API 中有一个 OfType() 方法...

IMongoCollection<Dog> dogs = db.GetCollection<Animal>("Animals").OfType<Dog>()

【讨论】:

    【解决方案2】:

    好的,这似乎确实有效:

    var catQuery = Builders<Animal>.Filter.Eq(nameof(Cat.LikesFish), true);
    var dogQuery = Builders<Animal>.Filter.Eq(nameof(Dog.FavouriteBone), "Beef");
    var query = Builders<Animal>.Filter.Or(catQuery, dogQuery);
    var catsThatLikeFishOrDogsThatLikeBeef = animals.FindSync(query).ToList();
    

    虽然我确实认为它缺乏旧驱动方法的优雅。

    【讨论】:

    • 这与我向您建议的解决方案完全相同,那里出了什么问题?
    • 没有错,我将这个具体答案发布为已确认的解决方案,感谢您的帮助。
    猜你喜欢
    • 2012-03-04
    • 2020-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 2016-05-10
    • 2012-09-02
    相关资源
    最近更新 更多