If you have ever had the need to store one or more possible values in a single field, you may have used the [Flags] attribute on an enum in C# which would look something like this:
The numeric values that we assign are important, because it allows us to do bit-wise operations on the number. The values themselves can easily be calculated by raising 2 to the power of a zero-based sequence of numbers. So this:
2⁰, 2¹, 2², 2³, 2⁴, 2⁵, 2⁶
yields this (yes, 2 to the zero power is one):
1, 2, 4, 8, 16, 32, 64
When we talk about doing a bit-wise comparison, I find it easier to understand when I see these numbers written in binary:
As you can see, each of these numbers hold only a single 1 value in each position. This is what will allow us to store any combination of these values and still determine which of the individual values a number contains. For example, the value 3 (0000011) indicates that the two first options are set (Red and Orange).
When we store this value in the database, it is simply a number and for our example, we will never exceed the sum of those values (1111111 in binary, or 127 in decimal) so we could store it as a tinyint, but a smallint would give us room to add more values without the need to change our data type.
If we are only working with our Flags Enum in C#, we can use the Enum.HasFlag() method to determine if our value has a certain enum value:
In the method above, we use a bit-wise OR to combine the values of the primary colors and then use HasFlag() to see if the color passed into the method matches any of the binary values of the primary colors. And since we are using a ColorFlags enum as the parameter, you can even use HasFlag to compare any combination of values.
Flags and Entity Framework
This all works great in C#, but we have to take a different approach if we are using Linq to Entities because HasFlag() is not available to us. Fortunately, SQL Server and Entity Framework support bit-wise operations, so we can use the single “&” to see if the stored value has the bit position that matches our value.
The example below shows how we would query for records that match ALL of the specified colors. It will include records that may have additional colors specified, but they must have at a minimum Blue and Red set. This is because we are comparing the AND-ed bits against the original colorsToMatch value:
If we instead want to return cars that have ANY of the specified colors, we just need to check to see if the & operation is not zero. If any of the bit positions match, it will return the sum of those values, but we don’t care if it is exactly the same as the colorsToMatch value, only that it isn’t zero meaning that there is at least one color that matches:
Additional Recommendations
A few things to keep in mind when using Flag Enums with Entity Framework:
- Start your Flags Enum values with one, not zero. Otherwise, you won’t be able to match the first value. The exception is if you want to provide a “None” option.
- Remember that your data will be stored as a number in the database so use a datatype that can accommodate all of your values with room to add more, if necessary.
- Be aware of the differences between matching ANY of the flag values vs. ALL of the flag values.