To enable Code First Migrations in entity framework, use the command
Enable-Migrations
on the Package Manager Console.
You need to have a valid DbContext
implementation containing your database objects managed by EF. In this example the database context will contain to objects BlogPost
and Author
:
internal class DatabaseContext: DbContext
{
public DbSet<Author> Authors { get; set; }
public DbSet<BlogPost> BlogPosts { get; set; }
}
After executing the command, the following output should appear:
PM> Enable-Migrations
Checking if the context targets an existing database...
Code First Migrations enabled for project <YourProjectName>.
PM>
In addition, a new folder Migrations
should appear with a single file Configuration.cs
inside:
The next step would be to create your first database migration script which will create the initial database (see next example).
After you've enabled migrations (please refer to this example) you are now able to create your first migration containing an initial creation of all database tables, indexes and connections.
A migration can be created by using the command
Add-Migration <migration-name>
This command will create a new class containing two methods Up
and Down
that are used to apply and remove the migration.
Now apply the command based on the example above to create a migration called Initial:
PM> Add-Migration Initial
Scaffolding migration 'Initial'.
The Designer Code for this migration file includes a snapshot of your current Code
First model. This snapshot is used to calculate the changes to your model when you
scaffold the next migration. If you make additional changes to your model that you
want to include in this migration, then you can re-scaffold it by running
'Add-Migration Initial' again.
A new file timestamp_Initial.cs is created (only the important stuff is shown here):
public override void Up()
{
CreateTable(
"dbo.Authors",
c => new
{
AuthorId = c.Int(nullable: false, identity: true),
Name = c.String(maxLength: 128),
})
.PrimaryKey(t => t.AuthorId);
CreateTable(
"dbo.BlogPosts",
c => new
{
Id = c.Int(nullable: false, identity: true),
Title = c.String(nullable: false, maxLength: 128),
Message = c.String(),
Author_AuthorId = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Authors", t => t.Author_AuthorId)
.Index(t => t.Author_AuthorId);
}
public override void Down()
{
DropForeignKey("dbo.BlogPosts", "Author_AuthorId", "dbo.Authors");
DropIndex("dbo.BlogPosts", new[] { "Author_AuthorId" });
DropTable("dbo.BlogPosts");
DropTable("dbo.Authors");
}
As you can see, in method Up()
two tables Authors
and BlogPosts
are created and the fields are created accordingly. In addition, the relation between the two tables is created by adding the field Author_AuthorId
. On the other side the method Down()
tries to reverse the migration activities.
If you feel confident with your migration, you can apply the migration to the database by using the command:
Update-Database
All pending migrations (in this case the Initial-migration) are applied to the database and afterwards the seed method is applied (the appropriate example)
PM> update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target
database.
Applying explicit migrations: [201609302203541_Initial].
Applying explicit migration: 201609302203541_Initial.
Running Seed method.
You can see the results of the activities in the SQL explorer:
For the commands Add-Migration
and Update-Database
several options are available which can be used to tweak the activities. To see all options, please use
get-help Add-Migration
and
get-help Update-Database
After enabling and creating migrations there might be a need to initially fill or migrate data in your database. There are several possibilities but for simple migrations you can use the method 'Seed()' in the file Configuration created after calling enable-migrations
.
The Seed()
function retrieves a database context as it's only parameter and you are able to perform EF operations inside this function:
protected override void Seed(Model.DatabaseContext context);
You can perform all types of activities inside Seed()
. In case of any failure the complete transaction (even the applied patches) are being rolled back.
An example function that creates data only if a table is empty might look like this:
protected override void Seed(Model.DatabaseContext context)
{
if (!context.Customers.Any()) {
Customer c = new Customer{ Id = 1, Name = "Demo" };
context.Customers.Add(c);
context.SaveData();
}
}
A nice feature provided by the EF developers is the extension method AddOrUpdate()
. This method allows to update data based on the primary key or to insert data if it does not exist already (the example is taken from the generated source code of Configuration.cs):
protected override void Seed(Model.DatabaseContext context)
{
context.People.AddOrUpdate(
p => p.FullName,
new Person { FullName = "Andrew Peters" },
new Person { FullName = "Brice Lambson" },
new Person { FullName = "Rowan Miller" }
);
}
Please be aware that
Seed()
is called after the last patch has been applied. If you need to migration or seed data during patches, other approaches need to be used.
For example: You are going to migrate an existing column from non-required to required. In this case you might need to fill some default values in your migration for rows where the altered fields are actually NULL
. In case the default value is simple (e.g. "0") you might use a default
or defaultSql
property in your column definition. In case it's not so easy, you may use the Sql()
function in Up()
or Down()
member functions of your migrations.
Here's an example. Assuming a class Author which contains an email-address as part of the data set. Now we decide to have the email-address as a required field. To migrate existing columns the business has the smart idea of creating dummy email-addresses like [email protected]
, where full name is the authors full name without spaces. Adding the [Required]
attribute to the field Email
would create the following migration:
public partial class AuthorsEmailRequired : DbMigration
{
public override void Up()
{
AlterColumn("dbo.Authors", "Email", c => c.String(nullable: false, maxLength: 512));
}
public override void Down()
{
AlterColumn("dbo.Authors", "Email", c => c.String(maxLength: 512));
}
}
This would fail in case some NULL fields are inside the database:
Cannot insert the value NULL into column 'Email', table 'App.Model.DatabaseContext.dbo.Authors'; column does not allow nulls. UPDATE fails.
Adding the following like before the AlterColumn
command will help:
Sql(@"Update dbo.Authors
set Email = REPLACE(name, ' ', '') + N'@example.com'
where Email is null");
The update-database
call succeeds and the table looks like this (example data shown):
You may use the Sql()
function for all types of DML and DDL actibities in your database. It is executed as part of the migration transaction; If the SQL fails, the complete migration fails and a rollback is done.
Applications running in non-development environments often require database updates. After using the Add-Migration
command to create your database patches there's the need to run the updates on other environments, and then the test environment as well.
Challenges commonly faced are:
A workaround is the following code sequence which checks for updates to be performed, and executes them in order. Please ensure proper transactions & exception handling to ensure no data gets lost in case of errors.
void UpdateDatabase(MyDbConfiguration configuration) {
DbMigrator dbMigrator = new DbMigrator( configuration);
if ( dbMigrator.GetPendingMigrations().Any() )
{
// there are pending migrations run the migration job
dbMigrator.Update();
}
}
where MyDbConfiguration
is your migration setup somewhere in your sources:
public class MyDbConfiguration : DbMigrationsConfiguration<ApplicationDbContext>
Install-Package EntityFramework
in "Package Manager Console"providerName="System.Data.SqlClient"
in your connection.Blog
"BlogContext
"public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BlogContext: DbContext
{
public BlogContext(): base("name=Your_Connection_Name")
{
}
public virtual DbSet<Blog> Blogs{ get; set; }
}
Enable-Migration
command , This will create a migration folder in your projectAdd-Migration Your_Arbitrary_Migraiton_Name
command , this will create a migration class in migrations folder with two method Up() and Down()Update-Database
command in order to create a database with a blog table