Bitwise enum (flags) query using MongoDB's official C# driver
When I try to run a LINQ query of the form:
MongoCollection<MyEntity> collection;
collection.AsQueryable().Where(entity =>
(entity.Flags & MyFlags.AFlag) != MyFlags.None);
I get an ArgumentException
with the message Unsupported where clause: ((Int32)((Int32)entity.Flags & 4) != 0).
Is this a known bug/feature?
Is there any workaround?
From the documentation it seems like MongoDB has a bitwise update, but not a bitwise query.
For comparison, the same query runs smoothly above Redis using ServiceStack as a client.
I did find these two links (link1, link2) which suggest using JavaScript, however, that would make the implementation of the service layer very dependant on the DB technology.
My solution has two parts. I made a serializer for Enum flags that stores all the values in a list of strings. I made an extension method for Linq to "inject" the mongo query i need.
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());
}
That way, its both readable and i don't need to deserialize all the objects into memory.
PS: The GetPropertyName method is just a type safe way to get the property name:
public static string GetPropertyName<TClass, TProperty>(
Expression<Func<TClass, TProperty>> entityPropertyExpression)
{
return ((MemberExpression)entityPropertyExpression.Body).Member.Name;
}
Starting with MongoDB v 3.2 you can use bitsAllSet or bitsAnySet depending on what are you searching for.
So, with C# MongoDB Driver:
//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));
链接地址: http://www.djcxy.com/p/61946.html