nhibernate-core
nhibernate-core copied to clipboard
NH-3365 - Support for HasFlag method for enums with Flags attribute applied.
Andrey Kozhyn created an issue — :
When HasFlag method is called for any enum System.NotSupportedException is thrown for such query:
session.Query<Entity>().FirstOrDefault(x => x.EnumProperty.HasFlag(TestEnum.Value));
Alexander Zaytsev added a comment — :
Does EntityFramework or Linq2Sql support this?
Andrey Kozhyn added a comment — :
Checked for EntityFramework. It doesn't support it.
Andreas Eriksson added a comment — :
Entity Framework has support for it since 6.1 http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/3951293-support-enum-hasflag
Is there a prevision of when and which version it will be added?
By the way, I'm using NHibernate + OData, so I need the $filter=enumProperty has 'enumValue' to work correctly. For these cases when I can't just change the call of .Where(x => x.EnumProperty.HasFlag(EnumValue)) I made an ExpressionVisitor that changes x.EnumProperty.HasFlag(EnumValue) to ((x.EnumProperty & EnumValue) != 0)
EDIT:
I realize that this change does not consider aggregate flags like enum EnumX { A = 1, B = 2, AandB = 3, C = 4 }. The HasFlag method is more like ((x.EnumProperty & EnumValue) == EnumValue), so I change the code of NotEqual 0 to Equal EnumValue
public class ResolveHasFlagVisitor : ExpressionVisitor
{
protected static MethodInfo hasFlagMethod = typeof(Enum).GetMethod("HasFlag");
protected Expression VisitCallHasFlag(MethodCallExpression node)
{
var op1 = node.Object; // get the (x.PropertyValue)
var op2 = node.Arguments.First() as UnaryExpression; // get the Convert expression generated by HasFlag method
var enumValue = Expression.Convert(op2.Operand, Enum.GetUnderlyingType(op2.Operand.Type)); // create a Convert(enumValue, Int32) expression
var propertyValue = Expression.Convert(op1, Enum.GetUnderlyingType(op1.Type)); // create a Convert(propertyValue, Int32) expression
var op1Andop2 = Expression.And(propertyValue, enumValue); // create and (enumValue & propertyValue) expression
var nodeResult = Expression.Equal(op1Andop2, enumValue); // finally, create a (enumValue & propertyValue) == enumValue expression
return nodeResult;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method == hasFlagMethod)
{
return VisitCallHasFlag(node);
}
return base.VisitMethodCall(node);
}
}
If the code has any problem, feel free to suggest some changes.