使用 Code First 数据库迁移

Posted 守候幸福

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 Code First 数据库迁移相关的知识,希望对你有一定的参考价值。

当 Entity Framework Code First 的数据模型发生改变时,默认会引发一个System.InvalidOperationException 的异常。解决方法是使用DropCreateDatabaseAlways 或DropCreateDatabaseIfModelChanges,让Entity Framework 自动将数据库删除,然后重新创建。不过,这种方式过于残暴,应该使用更人性化的方式,让Entity Framework 帮助我们自动调整数据库架构。并且仍然保留现有数据库中的数据。而这种开发技术就是  Code First 数据库迁移(DB Migration)。

首先,我们先用 Code First 方式建立一个简单的ASP.NET MVC4 应用程序

技术分享

技术分享

 

在Models 文件夹下建立两个实体类Member、Guestbook。

Member 实体类定义如下:

  1. namespace CodeFirstDemo.Models  
  2. {  
  3.     public partial class Member  
  4.     {  
  5.         public Member()  
  6.         {  
  7.             this.Guestbooks = new List<Guestbook>();  
  8.         }  
  9.   
  10.         public int Id { get; set; }  
  11.         public string Name { get; set; }  
  12.         public string Email { get; set; }  
  13.         public virtual ICollection<Guestbook> Guestbooks { get; set; }  
  14.     }  
  15. }  
技术分享
namespace CodeFirstDemo.Models
{
    public partial class Member
    {
        public Member()
        {
            this.Guestbooks = new List<Guestbook>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public virtual ICollection<Guestbook> Guestbooks { get; set; }
    }
}

Guestbook 实体类定义如下:

  1. namespace CodeFirstDemo.Models  
  2. {  
  3.     public partial class Guestbook  
  4.     {  
  5.         public int Id { get; set; }  
  6.         public string Message { get; set; }  
  7.         public System.DateTime CreatedOn { get; set; }  
  8.         public int MemberId { get; set; }  
  9.         public virtual Member Member { get; set; }  
  10.     }  
  11. }  
技术分享
namespace CodeFirstDemo.Models
{
    public partial class Guestbook
    {
        public int Id { get; set; }
        public string Message { get; set; }
        public System.DateTime CreatedOn { get; set; }
        public int MemberId { get; set; }
        public virtual Member Member { get; set; }
    }
}

在Models 文件夹下建立Mapping 文件夹,并建立对应实体类的关系映射类MemberMap 、GuestbookMap

 

MemberMap 类定义如下:

  1. namespace CodeFirstDemo.Models.Mapping  
  2. {  
  3.     public class MemberMap : EntityTypeConfiguration<Member>  
  4.     {  
  5.         public MemberMap()  
  6.         {  
  7.             // Primary Key  
  8.             this.HasKey(t => t.Id);  
  9.   
  10.             // Properties  
  11.             this.Property(t => t.Id)  
  12.                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);  
  13.   
  14.             this.Property(t => t.Name)  
  15.                 .IsRequired()  
  16.                 .HasMaxLength(10);  
  17.   
  18.             this.Property(t => t.Email)  
  19.                 .IsRequired()  
  20.                 .HasMaxLength(200);  
  21.   
  22.             // Table & Column Mappings  
  23.             this.ToTable("Member");  
  24.             this.Property(t => t.Id).HasColumnName("Id");  
  25.             this.Property(t => t.Name).HasColumnName("Name");  
  26.             this.Property(t => t.Email).HasColumnName("Email");  
  27.         }  
  28.     }  
  29. }  
技术分享
namespace CodeFirstDemo.Models.Mapping
{
    public class MemberMap : EntityTypeConfiguration<Member>
    {
        public MemberMap()
        {
            // Primary Key
            this.HasKey(t => t.Id);

            // Properties
            this.Property(t => t.Id)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            this.Property(t => t.Name)
                .IsRequired()
                .HasMaxLength(10);

            this.Property(t => t.Email)
                .IsRequired()
                .HasMaxLength(200);

            // Table & Column Mappings
            this.ToTable("Member");
            this.Property(t => t.Id).HasColumnName("Id");
            this.Property(t => t.Name).HasColumnName("Name");
            this.Property(t => t.Email).HasColumnName("Email");
        }
    }
}

GuestbookMap 类定义如下:

  1. namespace CodeFirstDemo.Models.Mapping  
  2. {  
  3.     public class GuestbookMap : EntityTypeConfiguration<Guestbook>  
  4.     {  
  5.         public GuestbookMap()  
  6.         {  
  7.             // Primary Key  
  8.             this.HasKey(t => t.Id);  
  9.   
  10.             // Properties  
  11.             this.Property(t => t.Message)  
  12.                 .IsRequired()  
  13.                 .HasMaxLength(200);  
  14.   
  15.             // Table & Column Mappings  
  16.             this.ToTable("Guestbook");  
  17.             this.Property(t => t.Id).HasColumnName("Id");  
  18.             this.Property(t => t.Message).HasColumnName("Message");  
  19.             this.Property(t => t.CreatedOn).HasColumnName("CreatedOn");  
  20.             this.Property(t => t.MemberId).HasColumnName("MemberId");  
  21.   
  22.             // Relationships  
  23.             this.HasRequired(t => t.Member)  
  24.                 .WithMany(t => t.Guestbooks)  
  25.                 .HasForeignKey(d => d.MemberId);  
  26.   
  27.         }  
  28.     }  
  29. }  
技术分享
namespace CodeFirstDemo.Models.Mapping
{
    public class GuestbookMap : EntityTypeConfiguration<Guestbook>
    {
        public GuestbookMap()
        {
            // Primary Key
            this.HasKey(t => t.Id);

            // Properties
            this.Property(t => t.Message)
                .IsRequired()
                .HasMaxLength(200);

            // Table & Column Mappings
            this.ToTable("Guestbook");
            this.Property(t => t.Id).HasColumnName("Id");
            this.Property(t => t.Message).HasColumnName("Message");
            this.Property(t => t.CreatedOn).HasColumnName("CreatedOn");
            this.Property(t => t.MemberId).HasColumnName("MemberId");

            // Relationships
            this.HasRequired(t => t.Member)
                .WithMany(t => t.Guestbooks)
                .HasForeignKey(d => d.MemberId);

        }
    }
}

在Models 建立数据库上下文类CodeFirstDemoContext

CodeFirstDemoContext 类定义如下:

  1. namespace CodeFirstDemo.Models  
  2. {  
  3.     public partial class CodeFirstDemoContext : DbContext  
  4.     {  
  5.         static CodeFirstDemoContext()  
  6.         {  
  7.             //Database.SetInitializer<CodeFirstDemoContext>(new DropCreateDatabaseIfModelChanges<CodeFirstDemoContext>());  
  8.         }  
  9.   
  10.         public CodeFirstDemoContext()  
  11.             : base("Name=CodeFirstDemoContext")  
  12.         {  
  13.         }  
  14.   
  15.         public DbSet<Guestbook> Guestbooks { get; set; }  
  16.         public DbSet<Member> Members { get; set; }  
  17.   
  18.         protected override void OnModelCreating(DbModelBuilder modelBuilder)  
  19.         {  
  20.             modelBuilder.Configurations.Add(new GuestbookMap());  
  21.             modelBuilder.Configurations.Add(new MemberMap());  
  22.         }  
  23.     }  
  24. }  
技术分享
namespace CodeFirstDemo.Models
{
    public partial class CodeFirstDemoContext : DbContext
    {
        static CodeFirstDemoContext()
        {
            //Database.SetInitializer<CodeFirstDemoContext>(new DropCreateDatabaseIfModelChanges<CodeFirstDemoContext>());
        }

        public CodeFirstDemoContext()
            : base("Name=CodeFirstDemoContext")
        {
        }

        public DbSet<Guestbook> Guestbooks { get; set; }
        public DbSet<Member> Members { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new GuestbookMap());
            modelBuilder.Configurations.Add(new MemberMap());
        }
    }
}

Models 文件夹结构下

技术分享

 

以上就是一个简单的 Code First 结构了

接下来在Web.config 添加数据库连接字符串

  1. <connectionStrings>  
  2.     <add name="CodeFirstDemoContext" connectionString="Data Source=vin-pc;Initial Catalog=CodeFirstDemo;Persist Security Info=True;User ID=sa;Password=123456;MultipleActiveResultSets=True"  
  3.       providerName="System.Data.SqlClient" />  
  4.   </connectionStrings>  
技术分享
<connectionStrings>
    <add name="CodeFirstDemoContext" connectionString="Data Source=vin-pc;Initial Catalog=CodeFirstDemo;Persist Security Info=True;User ID=sa;Password=123456;MultipleActiveResultSets=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

然后添加一个控制器HomeController

  1. namespace CodeFirstDemo.Controllers  
  2. {  
  3.     public class HomeController : Controller  
  4.     {  
  5.         //  
  6.         // GET: /Home/  
  7.   
  8.         public ActionResult Index()  
  9.         {  
  10.             CodeFirstDemoContext db = new CodeFirstDemoContext();  
  11.             Member member = new Member { Name = "tt", Email = "[email protected]" };  
  12.             db.Members.Add(member);  
  13.             db.SaveChanges();  
  14.   
  15.             return View();  
  16.         }  
  17.   
  18.     }  
  19. }  
技术分享
namespace CodeFirstDemo.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            CodeFirstDemoContext db = new CodeFirstDemoContext();
            Member member = new Member { Name = "tt", Email = "[email protected]" };
            db.Members.Add(member);
            db.SaveChanges();

            return View();
        }

    }
}

EF Code First 如何记录版本

当应用程序通过EF Code First 创建数据库后,在此数据库中讲会自动创建一个名为 dbo. __MigrationHistory 的系统数据表,如下图所示:

技术分享

 

打开dbo. __MigrationHistory 会发现三个字段:MigrationId 字段用来记录这次由 EFCode First 所创建的一个表示名称,也可以称为一个版本代码;Model 字段表示这次创建时的模型数据,这是由 Entity Framework 将所有数据模型串行化后的版本,所以看不出是什么;ProductVersion 字段表示当前使用的Entity Framework 版本,如下图所示:

技术分享

 

如果尚未启用数据库迁移功能,每次在应用程序运行时,都会对比程序中当前的数据模型,与数据库中dbo. __MigrationHistory 表的Model 字段中的值是否一致,如果不一致,默认就会发生异常。

如果启用数据库迁移功能之后,这个表就会开始记录每次数据模型变动的记录与版本。

启用数据库迁移

若要在项目中启用数据库迁移功能,必须先开启程序包管理器控制台(Package Manager Console)窗口,然后输入 Enable-Migrations指令,如下图:

技术分享

技术分享

 

运行 Enable-Migrations 指令的过程中, Visual Studio 会在项目里创建一个Migrations 目录,该目录下还创建有两个文件,201309120825043_InitialCreate.cs 、Configuration.cs,如下图:

技术分享

 

1.      201309120825043_InitialCreate.cs

在启用数据库迁移之前,由于已经通过 Code First 在数据库中创建好了相关的数据库结构,也创建了一个初始的dbo. __MigrationHistory 数据表,表中也有一条数据,这条数据的MigrationId值正好会等于文档名。VS会将dbo. __MigrationHistory 表的Model 值读出,并创建这个类的属性,其属性就是包含那次数据模型的完整描述。

  1. namespace CodeFirstDemo.Migrations  
  2. {  
  3.     using System;  
  4.     using System.Data.Entity.Migrations;  
  5.       
  6.     public partial class InitialCreate : DbMigration  
  7.     {  
  8.         public override void Up()  
  9.         {  
  10.             CreateTable(  
  11.                 "dbo.Guestbook",  
  12.                 c => new  
  13.                     {  
  14.                         Id = c.Int(nullable: false, identity: true),  
  15.                         Message = c.String(nullable: false, maxLength: 200),  
  16.                         CreatedOn = c.DateTime(nullable: false),  
  17.                         MemberId = c.Int(nullable: false),  
  18.                     })  
  19.                 .PrimaryKey(t => t.Id)  
  20.                 .ForeignKey("dbo.Member", t => t.MemberId, cascadeDelete: true)  
  21.                 .Index(t => t.MemberId);  
  22.               
  23.             CreateTable(  
  24.                 "dbo.Member",  
  25.                 c => new  
  26.                     {  
  27.                         Id = c.Int(nullable: false, identity: true),  
  28.                         Name = c.String(nullable: false, maxLength: 5),  
  29.                         Email = c.String(nullable: false, maxLength: 200),  
  30.                     })  
  31.                 .PrimaryKey(t => t.Id);  
  32.               
  33.         }  
  34.           
  35.         public override void Down()  
  36.         {  
  37.             DropIndex("dbo.Guestbook", new[] { "MemberId" });  
  38.             DropForeignKey("dbo.Guestbook", "MemberId", "dbo.Member");  
  39.             DropTable("dbo.Member");  
  40.             DropTable("dbo.Guestbook");  
  41.         }  
  42.     }  
  43. }  
技术分享
namespace CodeFirstDemo.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class InitialCreate : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.Guestbook",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        Message = c.String(nullable: false, maxLength: 200),
                        CreatedOn = c.DateTime(nullable: false),
                        MemberId = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.Id)
                .ForeignKey("dbo.Member", t => t.MemberId, cascadeDelete: true)
                .Index(t => t.MemberId);
            
            CreateTable(
                "dbo.Member",
                c => new
                    {
                        Id = c.Int(nullable: false, identity: true),
                        Name = c.String(nullable: false, maxLength: 5),
                        Email = c.String(nullable: false, maxLength: 200),
                    })
                .PrimaryKey(t => t.Id);
            
        }
        
        public override void Down()
        {
            DropIndex("dbo.Guestbook", new[] { "MemberId" });
            DropForeignKey("dbo.Guestbook", "MemberId", "dbo.Member");
            DropTable("dbo.Member");
            DropTable("dbo.Guestbook");
        }
    }
}

2.      Configuration.cs

这个类定义了运行数据库迁移时该有的行为。默认情况下,数据库并不会发生迁移动作,除非将 Configuration() 内的 AutomaticMigrationsEnabled 改为 true,才会让 CodeFirst 自动迁移数据库。

  1. namespace CodeFirstDemo.Migrations  
  2. {  
  3.     using System;  
  4.     using System.Data.Entity;  
  5.     using System.Data.Entity.Migrations;  
  6.     using System.Linq;  
  7.   
  8.     internal sealed class Configuration : DbMigrationsConfiguration<CodeFirstDemo.Models.CodeFirstDemoContext>  
  9.     {  
  10.         public Configuration()  
  11.         {  
  12.             AutomaticMigrationsEnabled = false;  
  13.         }  
  14.   
  15.         protected override void Seed(CodeFirstDemo.Models.CodeFirstDemoContext context)  
  16.         {  
  17.             //  This method will be called after migrating to the latest version.  
  18.   
  19.             //  You can use the DbSet<T>.AddOrUpdate() helper extension method   
  20.             //  to avoid creating duplicate seed data. E.g.  
  21.             //  
  22.             //    context.People.AddOrUpdate(  
  23.             //      p => p.FullName,  
  24.             //      new Person { FullName = "Andrew Peters" },  
  25.             //      new Person { FullName = "Brice Lambson" },  
  26.             //      new Person { FullName = "Rowan Miller" }  
  27.             //    );  
  28.             //  
  29.         }  
  30.     }  
  31. }  
技术分享
namespace CodeFirstDemo.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;

    internal sealed class Configuration : DbMigrationsConfiguration<CodeFirstDemo.Models.CodeFirstDemoContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(CodeFirstDemo.Models.CodeFirstDemoContext context)
        {
            //  This method will be called after migrating to the latest version.

            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
}

运行数据库迁移

下面来更改 Member 的数据模型,添加两个字段,分别是 UserName 和 Password 属性。

  1. namespace CodeFirstDemo.Models  
  2. {  
  3.     public partial class Member  
  4.     {  
  5.         public Member()  
  6.         {  
  7.             this.Guestbooks = new List<Guestbook>();  
  8.         }  
  9.   
  10.         public int Id { get; set; }  
  11.         public string Name { get; set; }  
  12.         public string Email { get; set; }  
  13.         public string UserName { get; set; }  
  14.         public string Password { get; set; }  
  15.         public virtual ICollection<Guestbook> Guestbooks { get; set; }  
  16.     }  
  17. }  
技术分享
namespace CodeFirstDemo.Models
{
    public partial class Member
    {
        public Member()
        {
            this.Guestbooks = new List<Guestbook>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public virtual ICollection<Guestbook> Guestbooks { get; set; }
    }
}

通过Package Manager Console 输入 Add-Migration 指令,来新增一条数据库迁移版本,输入时必须带上一个“版本名称”参数。例如,想要取名为AddUsernamePassword,则可以输入以下指令:

技术分享

运行完成后,会在 Migrations 文件夹新增一个文件,如下图:

技术分享

 

这次运行 Add-Migration 指令,所代表的意思就是新增一次运行数据库迁移命令,VS2012会自动对比当前数据库中的 Model 定义与当前更改过的数据模型,并将差异的字段变化写入这个自动新增的类内,程序代码如下:

  1. namespace CodeFirstDemo.Migrations  
  2. {  
  3.     using System;  
  4.     using System.Data.Entity.Migrations;  
  5.       
  6.     public partial class AddUsernamePassword : DbMigration  
  7.     {  
  8.         public override void Up()  
  9.         {  
  10.             AddColumn("dbo.Member", "UserName", c => c.String());  
  11.             AddColumn("dbo.Member", "Password", c => c.String());  
  12.         }  
  13.           
  14.         public override void Down()  
  15.         {  
  16.             DropColumn("dbo.Member", "Password");  
  17.             DropColumn("dbo.Member", "UserName");  
  18.         }  
  19.     }  
  20. }  
技术分享
namespace CodeFirstDemo.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class AddUsernamePassword : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Member", "UserName", c => c.String());
            AddColumn("dbo.Member", "Password", c => c.String());
        }
        
        public override void Down()
        {
            DropColumn("dbo.Member", "Password");
            DropColumn("dbo.Member", "UserName");
        }
    }
}

NOTES

每一次新增数据库迁移版本,其类内都会包含一个Up() 方法与 Down() 方法,所代表的意思分别是“升级数据库”与“降级数据库”的动作,所以,数据库迁移不仅仅只是将数据库升级,还可以恢复到旧版本。

当前还没有对数据库做任何迁移动作,所以数据库中的数据结构并没有任何改变,现在,手动在 Member 数据表中输入几条数据,以确认待会儿数据库迁移(升级)之后数据是否消失,如图:

技术分享

 

接着,对数据库进行迁移动作,在程序包管理控制台(Package Manager Console)窗口中输入Update-Database指令,如图:

技术分享

 

更新数据库成功之后,可以查看 Member  数据表结构是否发生变化,以及数据表原来的数据是否存在:

技术分享

 

NOTES

我们都知道,在客户端数据库通常是无法直接联机的,客户的生产环境通常也没有安装VS2012,那么如果数据库迁移动作要进行套用时,应该怎么办呢?可以通过 Update-Database 指令的其他参数自动生产数据库迁移的 T-SQL 脚本,然后携带 T-SQL 脚本文件到正式主机部署或更新即可。

Update-Database 指令的–SourceMigration 参数可以指定来源斑斑驳驳,-Targetigration 参数可以指定目标版本, -Script 参数可以用来输出 T-SQL 脚本。以下是生成本次数据库迁移(升级)的 T-SQL 指令演示:

Update-Database –SourceMigration201309120825043_InitialCreate –TargetMigration 201309130055351_AddUsernamePassword-Script

如果要生成数据库降级的 T-SQL,则不能使用–SourceMigration 参数,直接指定–TargetMigration 参数即可,演示如下:

Update-Database –TargetMigration201309120825043_InitialCreate –Script

如果要还原数据库带添加 Code First 之前的初始状态,可以输入以下指令:

Update-Database  -TragetMigration:$InitialDatabase –Script

自定义数据库迁移规则

当了解数据库迁移的规则之后,如果希望在数据库迁移的过程中进行一些微调,例如, Entity Framework 并不支持自动设置字段的默认值,假设我们在 Member 数据模型中想添加一个新的 CreatedOn 属性表示会员的注册日期,并且希望在数据库中自动加上 getdate() 默认值,这时就必须要自定义数据库迁移的规则。

首先更改 Member 数据模型,加上 CreatedOn 属性

 

 

Member.cs

  1. namespace CodeFirstDemo.Models  
  2. {  
  3.     public partial class Member  
  4.     {  
  5.         public Member()  
  6.         {  
  7.             this.Guestbooks = new List<Guestbook>();  
  8.         }  
  9.   
  10.         public int Id { get; set; }  
  11.         public string Name { get; set; }  
  12.         public string Email { get; set; }  
  13.         public string UserName { get; set; }  
  14.         public string Password { get; set; }  
  15.         public DateTime CreatedOn { get; set; }  
  16.         public virtual ICollection<Guestbook> Guestbooks { get; set; }  
  17.     }  
  18. }  
技术分享
namespace CodeFirstDemo.Models
{
    public partial class Member
    {
        public Member()
        {
            this.Guestbooks = new List<Guestbook>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public DateTime CreatedOn { get; set; }
        public virtual ICollection<Guestbook> Guestbooks { get; set; }
    }
}

MemberMap.cs

  1. namespace CodeFirstDemo.Models.Mapping  
  2. {  
  3.     public class MemberMap : EntityTypeConfiguration<Member>  
  4.     {  
  5.         public MemberMap()  
  6.         {  
  7.             // Primary Key  
  8.             this.HasKey(t => t.Id);  
  9.   
  10.             // Properties  
  11.             this.Property(t => t.Id)  
  12.                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);  
  13.   
  14.             this.Property(t => t.Name)  
  15.                 .IsRequired()  
  16.                 .HasMaxLength(10);  
  17.   
  18.             this.Property(t => t.Email)  
  19.                 .IsRequired()  
  20.                 .HasMaxLength(200);  
  21.   
  22.             this.Property(t => t.CreatedOn)  
  23.                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);  
  24.   
  25.             // Table & Column Mappings  
  26.             this.ToTable("Member");  
  27.             this.Property(t => t.Id).HasColumnName("Id");  
  28.             this.Property(t => t.Name).HasColumnName("Name");  
  29.             this.Property(t => t.Email).HasColumnName("Email");  
  30.         }  
  31.     }  
  32. }  
技术分享
namespace CodeFirstDemo.Models.Mapping
{
    public class MemberMap : EntityTypeConfiguration<Member>
    {
        public MemberMap()
        {
            // Primary Key
            this.HasKey(t => t.Id);

            // Properties
            this.Property(t => t.Id)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            this.Property(t => t.Name)
                .IsRequired()
                .HasMaxLength(10);

            this.Property(t => t.Email)
                .IsRequired()
                .HasMaxLength(200);

            this.Property(t => t.CreatedOn)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);

            // Table & Column Mappings
            this.ToTable("Member");
            this.Property(t => t.Id).HasColumnName("Id");
            this.Property(t => t.Name).HasColumnName("Name");
            this.Property(t => t.Email).HasColumnName("Email");
        }
    }
}

然后运行一次 Add-Migration指令,并指定版本名称为 AddMemberCreatedOn

技术分享

 

这时,再 Migrations 目录下多出一个201309130144538_AddMemberCreatedOn.cs 文件

  1. namespace CodeFirstDemo.Migrations  
  2. {  
  3.     using System;  
  4.     using System.Data.Entity.Migrations;  
  5.       
  6.     public partial class AddMemberCreatedOn : DbMigration  
  7.     {  
  8.         public override void Up()  
  9.         {  
  10.             AddColumn("dbo.Member", "CreatedOn", c => c.DateTime(nullable: false));  
  11.         }  
  12.           
  13.         public override void Down()  
  14.         {  
  15.             DropColumn("dbo.Member", "CreatedOn");  
  16.         }  
  17.     }  
  18. }  
技术分享
namespace CodeFirstDemo.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class AddMemberCreatedOn : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Member", "CreatedOn", c => c.DateTime(nullable: false));
        }
        
        public override void Down()
        {
            DropColumn("dbo.Member", "CreatedOn");
        }
    }
}

这次我们用不一样的参数来运行数据库迁移,加上–Script 参数,Update-Database –Script

运行完后,会输出完整的数据库更新 T-SQL 脚本,其中第一行就是在 Member 数据表中新增一个 CreatedOn 字段,而且会看到该字段已经给予‘1900-01-01T00:00:00.000’ 这个默认值。第二行则是在 _MigrationHistory新增一条版本记录,如下图:

技术分享

 

此时,可以自定义201309130144538_AddMemberCreatedOn.cs 类里的 Up() 方法,在新增字段的地方改用Sql()方法,传入一段自定义的 T-SQL 脚本来创建字段,并改用自己的方法新增字段,如此一来,即可让数据库迁移在升级是自动加上此字段的默认值。

  1. public override void Up()  
  2.         {  
  3.             //AddColumn("dbo.Member", "CreatedOn", c => c.DateTime(nullable: false));  
  4.             Sql("ALTER TABLE [dbo].[Member] ADD [CreatedOn] [datetime] NOT NULL DEFAULT getdate()");  
  5.         }  
技术分享
public override void Up()
        {
            //AddColumn("dbo.Member", "CreatedOn", c => c.DateTime(nullable: false));
            Sql("ALTER TABLE [dbo].[Member] ADD [CreatedOn] [datetime] NOT NULL DEFAULT getdate()");
        }

最后,运行 Update-Database 指令,这是再去检查 Member 数据表,可以看到,数据库迁移升级后的 CreatedOn 字段拥有了我们想要的 getdate() 默认值,如下图:

技术分享

 

TIPS

在数据库迁移类中除了有 Up() 方法外,还有 Down() 方法,必须留意当降级时必要的架构的变更动作,如果自定义数据库迁移的规则写不好,可能会导致降级失败或数据库结构紊乱

 

自动数据库迁移

如果要启用自动数据库迁移的话,在Database.SetInitializer() 方法中使用System.Data.Entity.MigrateDatabaseToLatestVersion泛型类型,并且传入两个参数,第一个是数据上下文类,第二个是在启用数据库迁移时自动生成的 Configuration 类,这个类喂鱼 Migrations 目录下,所以记得要加上命名空间:

  1. Database.SetInitializer(new MigrateDatabaseToLatestVersion<CodeFirstDemoContext, Migrations.Configuration>());  
技术分享
Database.SetInitializer(new MigrateDatabaseToLatestVersion<CodeFirstDemoContext, Migrations.Configuration>());

接着再开启Migrations\Configuration.cs 设置AutomaticMigrationsEnbaled 属性为 ture 即可

  1. AutomaticMigrationsEnabled = true;  
技术分享
AutomaticMigrationsEnabled = true;

如此一来,日后所以的数据模型变动时,都会通过数据库迁移功能自动升级数据库,当每次自动升级发生时,也会在 dbo._MigrationHistory 系统数据表里记录,并以AutomaticMigration 命名,如下图:

技术分享

 

如何避免数据库被自动创建或自动迁移

 

如果想要避免数据库被自动创建或自动迁移,则修改Database.SetInitializer() 方法,如:

  1. Database.SetInitializer<CodeFirstDemoContext>(null);  
技术分享
Database.SetInitializer<CodeFirstDemoContext>(null);

即可避免数据库被自动创建或自动迁移。

以上是关于使用 Code First 数据库迁移的主要内容,如果未能解决你的问题,请参考以下文章

使用 Entity Framework Code First 迁移播种大型查找表数据

EF Code First 数据迁移

使用Code First迁移添加外键

使用 Code First 数据库迁移

Code First 迁移

用于 ASP.NET Core 应用程序的 EF Code First 迁移替代方案