问题
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.
回答1:
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.
P.S: 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;
}
回答2:
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));
来源:https://stackoverflow.com/questions/17415046/bitwise-enum-flags-query-using-mongodbs-official-c-sharp-driver