5

当我尝试运行以下形式的 LINQ 查询时:

MongoCollection<MyEntity> collection;

collection.AsQueryable().Where(entity =>
    (entity.Flags & MyFlags.AFlag) != MyFlags.None);

我收到一条ArgumentException消息Unsupported where clause: ((Int32)((Int32)entity.Flags & 4) != 0).

这是一个已知的错误/功能吗?

有什么解决方法吗?

文档来看,MongoDB 似乎有按位更新,但没有按位查询。

作为比较,相同的查询在使用 ServiceStack 作为客户端的 Redis 上运行顺畅。

我确实发现这两个链接(链接1 链接2)建议使用JavaScript,但是,这将使服务层的实现非常依赖于数据库技术。

4

2 回答 2

11

从 MongoDB v 3.2 开始,您可以根据要搜索的内容使用bitsAllSetbitsAnySet 。

因此,使用 C# MongoDB 驱动程序:

//Check single Flag as OP
collection.Find(Builders<MyEntity>.Filter.BitsAllSet(myEntity => myEntity.Flags, (long) MyFlags.AFlag));

//Check all multiple Flags
collection.Find(Builders<MyEntity>.Filter.BitsAllSet(myEntity => myEntity.Flags, (long) MyFlags.AFlag | MyFlags.BFlag));

//Check any multiple Flag
collection.Find(Builders<MyEntity>.Filter.BitsAnySet(myEntity => myEntity.Flags, (long) MyFlags.AFlag | MyFlags.BFlag));

注意:在按位运算中使用索引存在一个未解决的问题。在 MongoDB 4.2 上,他们仍然不使用它们,所以如果你打算大量使用它们,请注意(我们曾经并且我们必须在之后重构所有内容)。

于 2016-06-14T12:03:19.080 回答
4

我的解决方案有两个部分。我为 Enum 标志制作了一个序列化程序,它将所有值存储在字符串列表中。我为 Linq 做了一个扩展方法来“注入”我需要的 mongo 查询。

public static IQueryable<TItem> HasFlags<TItem, TProperty>(
    this IQueryable<TItem> items,
    Expression<Func<TItem, TProperty>> itemPropertyExpression,
    params Enum[] enumFlags)
{
    var enumFlagNames = enumFlags.Select(enumFlag => (BsonValue)enumFlag.ToString());
    return items.Where(item => Query.In(ExtendedObject.GetPropertyName(itemPropertyExpression), enumFlagNames).Inject());
}

这样,它既可读又我不需要将所有对象反序列化到内存中。

PS:GetPropertyName 方法只是获取属性名称的一种类型安全的方法:

public static string GetPropertyName<TClass, TProperty>(
    Expression<Func<TClass, TProperty>> entityPropertyExpression)
{
    return ((MemberExpression)entityPropertyExpression.Body).Member.Name;
}
于 2013-11-17T21:38:51.987 回答