Search code examples
entity-framework-core.net-5enum-flags

Using [Flags] with multiple values in .Net 5.0.4 Entity Framework query


I have an Enum with [Flags] attribute like this:

[Flags]

public enum FlagStatus
    
{
    
    Value1 = 1
    
  , Value2 = 2
    
  , Value3 = 4
    
}

And my query for EF like this:

x => x.Status.HasFlag(flagStatus)

Now if I set the flagStatus = FlagStatus.Value1 the query works fine since I set value to 1).

@__request_Status_0 = 1
WHERE ([o].[StatusId] & @__request_Status_0) = @__request_Status_0)

But if I set it to flagStatus = FlagStatus.Value1 | FlagStatus.Value3 the query returns no results as the translated SQL looks like this:

@__request_Status_0 = 5
WHERE ([o].[StatusId] & @__request_Status_0) = @__request_Status_0)

And since that's is not a valid Id int the Status field no results are returned.

So the question now is this: Isn't .HasFlag supposed to be supported by .Net5 EF or is bitwise operation for some reason limited to one value? And if so why have bitwise operations support at all?

I probably missed something, but I just don't see it.


Solution

  • Isn't .HasFlag supposed to be supported by .Net5 EF

    Supported means translated to SQL instead of throwing runtime exception, so apparently it is.

    is bitwise operation for some reason limited to one value

    No, it's not. But when used with multiple values, it has different meaning than what you seem to be expecting. The documentation of the Enum.HasFlags CLR method says that it returns

    true if the bit field or bit fields that are set in flag are also set in the current instance; otherwise, false.

    and then in remarks:

    The HasFlag method returns the result of the following Boolean expression.

    thisInstance And flag = flag

    which is exactly what EF Core is doing.

    Translating it to simple words, it checks if ALL bits in flags are present in value (the equivalent All operation for sets). While you seem to be expecting it to has Any semantics.

    Shortly, HasFlag is for All. There is no dedicated method for Any, so you should use its direct bitwise operation equivalent which is

    (value & flags) != 0
    

    In your sample

    x => (x.Status & flagStatus) != 0