Store user options using enumeration, bitwise operators, and EF Core

At times you need to store a set of options, settings, or choices for a user.
A common approach is to create three tables - Users, Options, and UserOptions -
to store the data involved. However, you can also use an alternate approach that
involves enumerations, bitwise operators, and EF core value converters. To that
end this article discusses such an approach with an example.
Example
Let's assume that you are developing an ASP.NET Core web application that
sends notifications to the end users. There are several types of notifications
supported by the application - Email, SMS, Popup, Calendar, and Private Message.
The application allows a user to decide how to receive the notifications. That
means a user can have one or more ways of receiving the notifications. For
example, a user can opt to receive only Email notifications whereas some other
user may opt for Email and SMS based notifications. You would like to capture
these user notification options / settings and store them in a SQL database.
A common approach to this problem is to create three tables - Users, Options,
and UserOptions. This approach is great when there is unlimited number of
options and there could be any combination of users and options. However, if you
have only limited number of options as in the example scenario described above,
you may want to avoid Options and UserOptions tables altogether.
Another approach could be to create as many columns in the Users table as the
number of notification Options and then assign True / False value to each
column. However, this will lead to a lot more columns to the Users table. If you
need to add a notification option in the future you will need to change database
schema as well as C# code.
Yet another approach is to use C# enumerations, bitwise operators, and EF
Core value converters. This approach works well when you have a small set of
options that are not going to change frequently. The remainder of this article
discusses this approach to accomplish the task.
Now that you have understood the example scenario and what we are trying to
accomplish, let's build an application that will demonstrate what we just
discussed.
Begin by creating and configuring a new ASP.NET Core web application.
Create the NotificationOptions enumeration
Now add a class named NotificationOptions in the Models folder. The
NotificationOptions class holds the possible notification options and is shown
below :
[Flags]
public enum NotificationOptions
{
Email = 1,
SMS = 2,
Popup = 4,
Calendar = 8,
PrivateMessage = 16
}
Notice a couple of things about this class. Firstly, it is decorated with
[Flags] attribute. The [Flags] attribute is used to indicate that the
enumeration values can be used with bitwise operators such as | and &. Secondly,
the enumeration values have been explicitly assigned to power of 2 (1, 2, 4, 8,
16). This is required for the proper functioning of bitwise operations.
Bitwise operations on NotificationOptions
Before we go ahead and see how to store notification options using Entity
Framework Core, it's worthwhile to see how bitwise operators can be used with
enumeration flags we just created.
Suppose we want to indicate that a user has two notification options - Email
and SMS. This is how you can represent them in your C# code :
NotificationOptions options =
NotificationOptions.Email | NotificationOptions.SMS;
Notice how the bitwise OR operator ( | ) is used to combine enumeration Email
and SMS flags. This way you can combine as many number of flags as you want and
store the result in options variable.
So far so good. But how to check whether a particular option exists in the
options thus formed? This can be done using bitwise AND ( & ) operator. The
following code shows how :
if((options & NotificationOptions.Email) !=0)
{
//email option exists!
}
if ((options & NotificationOptions.SMS) != 0)
{
//SMS option exists!
}
If we want to check whether Email notification is part of the selected
options, we use & operator and figure that out. Similarly you can check for
other notification options.
There is an alternate way to the above task. Instead of using the bitwise AND
operator you can use HasFlag() method of an enumeration as follows :
if(options.HasFlag(NotificationOptions.Email))
{
//email option exists!
}
if (options.HasFlag(NotificationOptions.SMS))
{
//SMS option exists!
}
The HasFlag() method returns true if a flag is a part of the value being
tested, otherwise it returns false.
Convert enumeration values to integers in EF Core
The above code works with NotificationOptions enumeration. This is quite
alright in your C# code. But how would you save these enumeration values to the
database? Luckily, Entity Framework Core provides an easy way to convert
enumeration values to numbers (or strings) so that they can be easily stored to
a database. This conversion needs to be enabled for the required entities. Let's
see how.
Consider the following entity class named UserNotifications :
public class UserNotifications
{
public int Id { get; set; }
public string Name { get; set; }
public NotificationOptions Options { get; set; }
}
The UserNotifications class stores all the notification options for a user
and has three properties namely Id, Name, and Options. The Options property is
assigned using the bitwise OR operation as discussed earlier and can have one or
more notification options.
The following figure shows the schema of UserNotifications table from a SQL
Server database.

As you can see the Options column is an integer column. That means you will
need to convert to and from NotificationOptions to integer. Although by default
EF Core does that correctly in most of the cases, it's good to ensure that
appropriate values are being transferred from our C# code also. This is accomplished
using what is known as Value Converter.
The following code shows the AppDbContext that makes use of
EnumToNumberConverter inbuilt value converter.
public class AppDbContext : DbContext
{
protected override void OnConfiguring
(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
"data source=.;initial catalog=MyDatabase;integrated
security=true");
}
protected override void OnModelCreating
(ModelBuilder modelBuilder)
{
EnumToNumberConverter<NotificationOptions,int>
converter = new EnumToNumberConverter<NotificationOptions,int>();
modelBuilder.Entity<UserNotifications>()
.Property(e => e.Options)
.HasConversion(converter);
}
public DbSet<UserNotifications>
UserNotifications { get; set; }
}
Notice a few things about the AppDbContext class.
It sets the database connection string in the OnConfiguring() overridden
method. You can also pick this value from a configuration file. More interesting
is the OnModelCreating() overridden method.
Inside, we create an instance of EnumToNumberConverter class. The
EnumToNumberConverter converts an enumeration value to a number and vice a
versa. In this case we want to convert between NotificationOptions and an
integer and hence we pass the generic types as shown above.
The code then uses HasConversion() method and passes the
EnumToNumberConverter object to it. This way EF Core knows how to convert the
values assigned to the Options property.
Ok. Now we have all the pieces ready - NotificationOptions enumeration,
UserNotifications table, UserNotifications entity class, and AppDbContext class.
Save notification options to the database
Let's write some code that will add a few records to the UserNotifications
table. The following code from Index() action shows how that can be done :
using (AppDbContext db = new AppDbContext())
{
UserNotifications user1 = new UserNotifications();
user1.Name = "Nancy Davolio";
user1.Options = NotificationOptions.Email |
NotificationOptions.SMS;
UserNotifications user2 = new UserNotifications();
user2.Name = "Andrew Fuller";
user2.Options = NotificationOptions.Popup |
NotificationOptions.SMS | NotificationOptions.Calendar;
UserNotifications user3 = new UserNotifications();
user3.Name = "Janet Leverling";
user3.Options = NotificationOptions.Email;
db.UserNotifications.Add(user1);
db.UserNotifications.Add(user2);
db.UserNotifications.Add(user3);
db.SaveChanges();
}
The above code creates three UserNotifications entities and assigns their
Name and Options properties. Notice how multiple notification options have been
assigned using the OR operator as discussed earlier. The Add() method adds these
entities to the UserNotifications DbSet and SaveChanges() saves them to the
UserNotifications table.
Retrieve notification options from the database
Now let's read the saved data and conform the working of enumerations and
bitwise AND operator.
Add a new view model class to the Models folder named UserNotificationSummary.
public class UserNotificationSummary
{
public int Id { get; set; }
public string Name { get; set; }
public bool Email { get; set; }
public bool SMS { get; set; }
public bool Popup { get; set; }
public bool Calendar { get; set; }
public bool PrivateMessage { get; set; }
}
The UserNotificationSummary class is intended to hold all the notification
options for a user in Boolean properties.
Now add the following loop below the SaveChanges() call we wrote earlier :
List<UserNotifications> data =
db.UserNotifications.ToList();
List<UserNotificationSummary> model =
new List<UserNotificationSummary>();
foreach(var item in data)
{
UserNotificationSummary summary =
new UserNotificationSummary();
summary.Id = item.Id;
summary.Name = item.Name;
if((item.Options & NotificationOptions.Email) !=0)
{
summary.Email = true;
}
if ((item.Options & NotificationOptions.SMS) != 0)
{
summary.SMS = true;
}
if ((item.Options & NotificationOptions.Popup) != 0)
{
summary.Popup = true;
}
if ((item.Options & NotificationOptions.Calendar) != 0)
{
summary.Calendar = true;
}
if ((item.Options & NotificationOptions.PrivateMessage) != 0)
{
summary.PrivateMessage = true;
}
model.Add(summary);
}
return View(model);
The code retrieves all the records from the UserNotifications table. It also
creates an empty List of UserNotificationSummary to hold the resultant
data. A foreach iterates through the data and checks the notification options
for a user. This is done using a series of If statements that assign the Boolean
view model properties as per the outcome. The List of UserNotificationSummary is
then passed to the Index view.
The Index view simply outputs the model data as shown below :
@model List<UserNotificationSummary>
<h1>Summary</h1>
<table border="1" cellpadding="10">
<tr>
<th>Id</th>
<th>Name</th>
<th>Email</th>
<th>SMS</th>
<th>Popup</th>
<th>Calendar</th>
<th>Private Message</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>@item.Id</td>
<td>@item.Name</td>
<td>@item.Email</td>
<td>@item.SMS</td>
<td>@item.Popup</td>
<td>@item.Calendar</td>
<td>@item.PrivateMessage</td>
</tr>
}
</table>
The following figure shows a sample run of the application :

That's it for now! Keep coding!!