Multi-Tenant Migrations With Entity Framework 6.0 (Code First Model)

Introduction

 
Up to Entity Framework 5.0, there were only single migrations possible to manage a single DbContext (user model) per physical database. Now Entity Framework 6.0 supports multiple model migrations per physical database.
 
This feature of Entity Framework is formerly known as "Multi-Tenant Migrations" or "Multiple Contexts per Database".
 
Generally, a multi-tenant database is able to isolate various groups of tables that might to be used for different purposes or may be used for different applications. There are multiple ways to make the database multi-tenant. One of the common methods is to use a different schema name for a different group of tables.
 
Entity Framework Code First model migration allows us to create a new database or update the existing database based on model classes. Entity Framework 5.0 Code First migration only supports one DbContext per physical database. Entity Framework 6.0 onward, Code First migration supports multiple DbContexts per single physical database.
 
The following is the procedure to migrate a Code First model.
 
 
Syntax
 
Step 1
  • enable-migrations -ContextTypeName -DbContext-Name-with-Namespaces-MigrationsDirectory:-Migrations-Directory-Name-
Step 2
  • Add-Migration -configuration -DbContext-Migrations-Configuration-Class-with-Namespaces-Migrations-Name-
Step 3
  • Update-Database -configuration -DbContext-Migrations-Configuration-Class-with-Namespaces -Verbose
Important Note
 
The Connection String must be the same as the ADO.NET Connection String and also the name provided must be “System.Data.SqlClient” instead of “System.Data.EntityClient” that is used in a Database First model.
  1. <configuration>  
  2.     …..  
  3.     …..  
  4.     <connectionStrings>  
  5.         <add name="Entities" connectionString="Data Source=serverName;Initial Catalog=DatabaseName;Persist Security Info=True;User ID=sa;Password=password ;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />  
  6.     </connectionStrings>  
  7. </configuration> 
Example
 
Suppose I have two DbContexts (say EmployeeContext and OderContext) within the same project. The Model classes and DBContext class code is given below.
 
Model Classes (Employee context)
  1. public class DepartmentMaster {  
  2.     [Key]  
  3.     public int DepartmentId { get;  
  4.         set; }  
  5.     public string Code { get;  
  6.         set; }  
  7.     public string Name { get;  
  8.         set; }  
  9.     public List < EmployeeMaster > Employees { get;  
  10.         set; }  
  11. }  
  12.   
  13. public class EmployeeMaster {  
  14.     [Key]  
  15.     public int Employee { get;  
  16.         set; }  
  17.     public string Code { get;  
  18.         set; }  
  19.     public string Name { get;  
  20.         set; }  
  21.     public int DepartmentId { get;  
  22.         set; }  
  23.     public DepartmentMaster Department { get;  
  24.         set; }  
Employee DbContext
  1. public class EmployeeContext: DbContext {  
  2.     public EmployeeContext(): base("name=Entities") {  
  3.   
  4.     }  
  5.     public DbSet < DepartmentMaster > Departments { get;  
  6.         set; }  
  7.     public DbSet < EmployeeMaster > Employees { get;  
  8.         set; }  
Model Classes (Order context)
  1. public class OrderMaster {  
  2.     [Key]  
  3.     public int OrderId { get;  
  4.         set; }  
  5.     public DateTime OrderDate { get;  
  6.         set; }  
  7.     public double TotalAmount { get;  
  8.         set; }  
  9.     public List < OrderDetails > OrderDetails { get;  
  10.         set; }  
  11. }  
  12.   
  13. public class OrderDetails {  
  14.     [Key]  
  15.     public int OrderDetailId { get;  
  16.         set; }  
  17.     public int OrderId { get;  
  18.         set; }  
  19.     public string ItemName { get;  
  20.         set; }  
  21.     public int Quantity { get;  
  22.         set; }  
  23.     public string UnitPrice { get;  
  24.         set; }  
  25.     public double Amount { get;  
  26.         set; }  
  27.     public OrderMaster Order { get;  
  28.         set; }  
Order context
  1. public class OrderContext: DbContext {  
  2.     public OrderContext(): base("name=Entities") {  
  3.   
  4.     }  
  5.     public DbSet < OrderMaster > Orders { get;  
  6.         set; }  
  7.     public DbSet < OrderDetails > OrderDetails { get;  
  8.         set; }  
Graphical representation of the relationship
 
 
Code First migration Procedure
 
For migrating, the following commands must run using the Package Manager Console of Visual Studio (2012 or 2013).
 
Step 1
  • Enable migration for DbContext
enable-migrations -ContextTypeName MultipleContextsperDatabase.Model.EmployeeContext -MigrationsDirectory:EmployeeMigrations
 
Output on Package Manager Console
 
 
As a result of Step 1, the Configuration class is added within the EmployeeMigration folder.
 
 
Configuration class definition
  1. internal sealed class Configuration: DbMigrationsConfiguration < MultipleContextsperDatabase.Model.EmployeeContext > {  
  2.     public Configuration() {  
  3.         AutomaticMigrationsEnabled = false;  
  4.         MigrationsDirectory = @ "EmployeeMigrations";  
  5.     }  
  6.   
  7.     protected override void Seed(MultipleContextsperDatabase.Model.EmployeeContext context) {  
  8.   
  9.     }  
Step 2: Add migration configuration
 
Add-Migration -configuration MultipleContextsperDatabase.EmployeeMigrations.Configuration InitialEmployee
 
Output on Package Manager Console
 
 
As a result of Step 2, the Db migration file is generated within the “EmployeeMigration” folder. This file has a name suffix as the Migrations name followed by an underscore and a unique generated number. This file has all the entities to be created in the database.
 
 
Migration file Definition
  1. public partial class InitialEmployee: DbMigration {  
  2.     public override void Up() {  
  3.         CreateTable(  
  4.                 "dbo.DepartmentMasters",  
  5.                 c => new {  
  6.                     DepartmentId = c.Int(nullable: false, identity: true),  
  7.                         Code = c.String(),  
  8.                         Name = c.String(),  
  9.                 })  
  10.             .PrimaryKey(t => t.DepartmentId);  
  11.   
  12.         CreateTable(  
  13.                 "dbo.EmployeeMasters",  
  14.                 c => new {  
  15.                     Employee = c.Int(nullable: false, identity: true),  
  16.                         Code = c.String(),  
  17.                         Name = c.String(),  
  18.                         DepartmentId = c.Int(nullable: false),  
  19.                 })  
  20.             .PrimaryKey(t => t.Employee)  
  21.             .ForeignKey("dbo.DepartmentMasters", t => t.DepartmentId, cascadeDelete: true)  
  22.             .Index(t => t.DepartmentId);  
  23.   
  24.     }  
  25.   
  26.     public override void Down() {  
  27.         DropForeignKey("dbo.EmployeeMasters""DepartmentId""dbo.DepartmentMasters");  
  28.         DropIndex("dbo.EmployeeMasters"new [] { "DepartmentId" });  
  29.         DropTable("dbo.EmployeeMasters");  
  30.         DropTable("dbo.DepartmentMasters");  
  31.     }  
  32.  
Step 3: Update database
 
Update-Database -configuration:MultipleContextsperDatabase.EmployeeMigrations.Configuration -Verbose
 
Output on Package Manager Console
 
 
After executing the command above a new something will be created (or update the existing database) that is supplied in the initial catalog property of the Connection String.
 
 
A migration history table is a table that stores the details of migrations applied to the database in a Code First model. It means that the migration history table contains all the migration changes to the database. The default name of this table is _MigrationHistory and it is created when a first migration is applied to the database. In Entity Framework 5.0, this is a system table (If the application uses MSSQL). In the Entity Framework 6.0, this table is no longer marked as a system table.
 
Output of migration history table
 
 
The same procedure as described above must be followed for OderDbContext.
 
The following command must be run one by one by using the Package Manager Console of Visual Studio (2012 or 2013).
 
enable-migrations -ContextTypeName:MultipleContextsperDatabase.Model.OrderContext -MigrationsDirectory:OrderMigrations
 
Add-Migration -configuration MultipleContextsperDatabase.OrderMigrations.Configuration InitialOrder
 
Update-Database -configuration:MultipleContextsperDatabase.OrderMigrations.Configuration -Verbose
 
Configuration class definition (generated by Step-1)
  1. internal sealed class Configuration: DbMigrationsConfiguration < MultipleContextsperDatabase.Model.OrderContext > {  
  2.     public Configuration() {  
  3.         AutomaticMigrationsEnabled = false;  
  4.         MigrationsDirectory = @ "OrderMigrations";  
  5.     }  
  6.   
  7.     protected override void Seed(MultipleContextsperDatabase.Model.OrderContext context) {  
  8.   
  9.     }  
Migrations file Definition (Generated by Step-2)
  1. public partial class InitialOrder: DbMigration {  
  2.     public override void Up() {  
  3.         CreateTable(  
  4.                 "dbo.OrderDetails",  
  5.                 c => new {  
  6.                     OrderDetailId = c.Int(nullable: false, identity: true),  
  7.                         OrderId = c.Int(nullable: false),  
  8.                         ItemName = c.String(),  
  9.                         Quantity = c.Int(nullable: false),  
  10.                         UnitPrice = c.String(),  
  11.                         Amount = c.Double(nullable: false),  
  12.                 })  
  13.             .PrimaryKey(t => t.OrderDetailId)  
  14.             .ForeignKey("dbo.OrderMasters", t => t.OrderId, cascadeDelete: true)  
  15.             .Index(t => t.OrderId);  
  16.   
  17.         CreateTable(  
  18.                 "dbo.OrderMasters",  
  19.                 c => new {  
  20.                     OrderId = c.Int(nullable: false, identity: true),  
  21.                         OrderDate = c.DateTime(nullable: false),  
  22.                         TotalAmount = c.Double(nullable: false),  
  23.                 })  
  24.             .PrimaryKey(t => t.OrderId);  
  25.   
  26.     }  
  27.   
  28.     public override void Down() {  
  29.         DropForeignKey("dbo.OrderDetails""OrderId""dbo.OrderMasters");  
  30.         DropIndex("dbo.OrderDetails"new [] { "OrderId" });  
  31.         DropTable("dbo.OrderMasters");  
  32.         DropTable("dbo.OrderDetails");  
  33.     }  
Table generate by the step-3
 
 
 
Output of migration history table
 
 
Undo/Rollback DbContexts Migrations
 
We can also roll back database changes using the following command.
 
Update-Database -configuration MultipleContextsperDatabase.EmployeeMigrations.Configuration -TargetMigration:"201407221118333_InitialEmployee" –verbose
 
Update-Database -configuration MultipleContextsperDatabase.OrderMigrations.Configuration -TargetMigration:"201407221128534_InitialOrder" -verbose
 
Output on Package Manager Console
 
 

Summary

 
Entity Framework has many new features, Multi-Tenant migration is one of them. With this feature, we can get the same functionality as a multi-tenant database. I hope this will help you.