nhibernate-core icon indicating copy to clipboard operation
nhibernate-core copied to clipboard

NH-3365 - Support for HasFlag method for enums with Flags attribute applied.

Open nhibernate-bot opened this issue 8 years ago • 1 comments

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

nhibernate-bot avatar Oct 12 '17 12:10 nhibernate-bot

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.

rodrigo-web-developer avatar Dec 29 '21 20:12 rodrigo-web-developer