Create a multitenant application with Entity Framework Code First - Part 1
A highly increasing request we have to serve as developers, especially after Software as a Service revolution, is to provide software that is able to handle individual users in one application by separate each user’s data. This feature is called Multitenancy and there are several ways to achieve it. In this series of posts I will try to demonstrate how we can achieve the data isolation by using some advance features of Entity Framework.
The really simple model
The first part includes the description of the model used across the application. Lets assume we want to manage some Messages for our application and each logged in user should be able to access and modify only his personal messages. The Message is a classic POCO class that doesn’t have any Entity Framework specific code as you can see in the snippet below.
We follow mainly the convention over configuration approach Entity Framework provides and the only explicit configuration we have to apply in our Message class is to declare TenantId as foreign key for User property by adding the code that follows in the DbContext class we are going to use for data access.
The “manual” approach
I will first implement an MVC Controller that includes the logic of filtering based on TenantId and assign the correct TenantId when creating a new message. The code we have to write is more or less like the snippet below:
I didn’t include all required logic and also removed the delete functionality but the problem of always remember to filter the correct data is obvious. On top of this we must assign the TenantId property explicitly as the user should not be able to do something like this. This approach is of course a valid solution but requires a lot of repetition of the same logic throughout our application something that is always error prone. On top of this the entire application has knowledge of the TenantId concept. We could solve this problem by creating a base class, something like base Repository or similar approach, and hide this detail there. This would definitely improve the design but still when the base class is not enough we have to remember to apply the filtering.
Entity Framework Interceptors
Entity Framework Interceptors is a very powerful feature added in version 6 of the framework and we can use them in order to hide the filtering and assign the correct TenantId in only one place. To be able to do that we must first write some infrastructure code.
TenantAwareAttribute and custom convention
The first step is to create an ordinary attribute class with which we can annotate our classes in order to get the Tenant functionality automatically. The code for the attribute class is shown below:
When using this attribute we have to declare which property of the class is the one responsible for TenantId. Moreover exposes a static helper method to get the the tenant column name based on the EdmType.
The next step is to use this attribute by adding a custom Entity Framework convention to the DbContext class we are going to use for data access in our application. To do this we have to add the code snippet below on the DbContext class.
Changes in Message class
After creating this infrastructure code the Message class can change and decorate it with the TenantAwareAttribute attribute as below:
As we can see here there is another important change in the class. The TenantId property now is private set which means we can’t explicitly assign it from our code base. The last issue is quite important as we are not able to assign an invalid TenantId by mistake. Furthermore we don’t have to take care of assign a TenantId at all.
This was the first part of a series of three posts. It includes the problem and the infrastructure code for what is coming on the next posts. For those that can’t wait I have created a project in Github that contains the full code in a relatively change model. In the next post I will describe how we can use the code we already created to apply filtering based on TenantId always in a transparent way for our application.