EntityFrameworkCore Migrations
Tools needed for the below demo:
VS 2017
.NET Framework
.NET Core 2.1
AdventureWorksDB - you can download it from the Microsoft site.
Packages to be installed are -
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools.DotNet
So, let us start now.
Take a .NET Core Console Project (EFCoreSample) and run the below command from Package Manager Console.
Scaffold-DbContext "Server=.\;Data Source=INHYLDEBASISHK;Initial Catalog=AdventureWorksLT2017;Integrated Security=False;Trusted_Connection=True;MultipleActiveResultSets=true;" Microsoft.EntityFrameworkCore.SqlServer -o Entities
Note that in EF Core, we don't have the edmx concept.
Output
The output of the above query will be an Entities folder with all the entities (Tables) of AdventureDB and contextClass.
For enabling the migration, run the below command in Package Manager Console (PMC).
add-migration migrationName
This adds a migration folder with 3 files. For example, I have run the command add-migration ProductModel. Look into the output on my system.
Naming
- Timestamp_migrationname.cs
- Timestamp_migrationname.designer.cs – metadata file , contains information used by EF core
- <contextclassname>modelsnapshot.cs
Timestamp_migrationname.cs
This is the main migration file which includes 2 methods, up and down. The Up method includes the code for creating DB objects and the down method includes code for removing the DB objects.
<contextclassname>modelsnapshot.cs
This takes the snapshot of a current model. This is used to determine what will be changed when creating the next migration.
Scenario
Suppose we want to add a record to the product Model table. We can create database scripts for the same requirement but here, instead of creating a database script, we will create migrations using EF Core.
- using System;
- using Microsoft.EntityFrameworkCore.Metadata;
- using Microsoft.EntityFrameworkCore.Migrations;
-
- namespace EFCoreSample.Migrations
- {
- public partial class ProductModel : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- var guid = Guid.NewGuid().ToString();
- migrationBuilder.Sql(@"
- IF NOT EXISTS(SELECT * FROM [SalesLT].[ProductModel] WHERE[Name] = N'SpeedoMeter') BEGIN
- INSERT [SalesLT].[ProductModel] ([Name], [CatalogDescription], [rowguid],[ModifiedDate]) VALUES
- (N'SpeedoMeter',NULL,NEWID(),GETDATE())
- END");
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
-
- }
- }
- }
Now, we will update the database from Visual Studio PMC. Before updating, we need to map the entities model name with the database table name. In the below diagram, you can see that the table name is prefixed with schema name SalesLT.ProductModel but our entity name is ProductModel.
So, we have a concept called the Table attribute.
The Table attribute is applied to an entity to specify the name of the database table that the entity should map to. The above example specifies that the product model entity should map to a database table named SalesLT.ProductModel.
So, in the Attribute folder, we can add a DBTableAttribute class which inherits from TableArribute and take care of these conversions.
DBTableAttribute.cs
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations.Schema;
- using System.Text;
-
- namespace EFCoreSample.Attibute
- {
- [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
- public class DbTableAttribute : TableAttribute
- {
- public DbTableAttribute(string name) : base(GetTableName(name))
- {
- Schema = GetSchema(name);
- }
-
- private static string GetSchema(string name)
- {
- var split = name.Split('.');
-
- if (split.Length == 1)
- return null;
-
- return split[0];
- }
-
- private static string GetTableName(string name)
- {
- var split = name.Split('.');
-
- if (split.Length == 1)
- return split[0];
-
- return split[1];
- }
- }
- }
Adding the attribute to the ProductModel Entity : (present inside Entities Folder)
- using EFCoreSample.Attibute;
- using Microsoft.EntityFrameworkCore;
- using System;
- using System.Collections.Generic;
-
- namespace EFCoreSample.Entities
- {
- [DbTable("SalesLT.ProductModel")]
- public partial class ProductModel
- {
- public ProductModel()
- {
- Product = new HashSet<Product>();
- ProductModelProductDescription = new HashSet<ProductModelProductDescription>();
- }
Creating or Updating the Database
For updating the database, run the below command in Package Manager Console(PMC).
Update-DataBase
- The database command will create a database based on the context and domain classes and the migration snapshot.
- When we run the update-database command after creating the first migration, a table also gets created in Db (_EFMigrationHistory) which will store all the names of the migrations, as and when they get applied to the database.
Whenever we run the update database command, it will apply all the migrations to the database.
Thanks!