实体框架 - 代码优先迁移



实体框架 4.3 包含一个新的代码优先迁移功能,允许您随着模型随时间变化而逐步发展数据库模式。对于大多数开发者来说,这比 4.1 和 4.2 版本中需要您手动更新数据库或在模型更改时删除并重新创建数据库的数据库初始化程序选项有了很大的改进。

  • 在实体框架 4.3 之前,如果您已经在数据库中拥有数据(种子数据除外)或现有的存储过程、触发器等,这些策略都会删除整个数据库并重新创建它,因此您会丢失数据和其他数据库对象。

  • 通过迁移,当您的模型发生更改时,它将自动更新数据库模式,而不会丢失任何现有数据或其他数据库对象。

  • 它使用一个名为 MigrateDatabaseToLatestVersion 的新的数据库初始化程序。

迁移有两种类型:

  • 自动迁移
  • 基于代码的迁移

自动迁移

自动迁移首先在实体框架 4.3 中引入。在自动迁移中,您不需要在代码文件中手动处理数据库迁移。例如,对于每次更改,您还需要更改域类。但是,使用自动迁移,您只需在程序包管理器控制台中运行一个命令即可完成此操作。

让我们看一下自动迁移的分步过程。

当您使用代码优先方法时,您的应用程序没有数据库。

在这个例子中,我们将从三个基本类开始,例如学生、课程和注册,如下代码所示。

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

以下是上下文类。

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

在运行应用程序之前,您需要启用自动迁移。

步骤 1 - 从工具 → NuGet 程序包管理器 → 程序包管理器控制台打开程序包管理器控制台。

步骤 2 - 要启用自动迁移,请在程序包管理器控制台中运行以下命令。

PM> enable-migrations -EnableAutomaticMigrations:$true
Command

步骤 3 - 命令成功运行后,它会在项目的 Migration 文件夹中创建一个内部密封的 Configuration 类,如下代码所示。

namespace EFCodeFirstDemo.Migrations {

   using System;
   using System.Data.Entity;
   using System.Data.Entity.Migrations;
   using System.Linq;
	
   internal sealed class Configuration : DbMigrationsConfiguration<EFCodeFirstDemo.MyContext> {

      public Configuration() {
         AutomaticMigrationsEnabled = true;
         ContextKey = "EFCodeFirstDemo.MyContext";
      }

      protected override void Seed(EFCodeFirstDemo.MyContext 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" }
         //  );
      }
   }
}

步骤 4 - 使用新的数据库初始化策略 MigrateDatabaseToLatestVersion 在上下文类中设置数据库初始化程序。

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, 
         EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}

步骤 5 - 您已设置自动迁移。当您执行应用程序时,当您更改模型时,它会自动处理迁移。

Migration

步骤 6 - 正如您所看到的,一个系统表 __MigrationHistory 也与其他表一起在您的数据库中创建。在 __MigrationHistory 中,自动迁移维护数据库更改的历史记录。

步骤 7 - 当您添加另一个实体类作为您的域类并执行您的应用程序时,它将在您的数据库中创建该表。让我们添加以下 StudentLogIn 类。

public class StudentLogIn {
   [Key, ForeignKey("Student")]
   public int ID { get; set; }
   public string EmailID { get; set; }
   public string Password { get; set; }
	
   public virtual Student Student { get; set; }
}

步骤 8 - 不要忘记在您的上下文类中添加上面提到的类的 DBSet,如下代码所示。

public virtual DbSet<StudentLogIn> StudentsLogIn { get; set; }

步骤 9 - 再次运行您的应用程序,您将看到 StudentsLogIn 表已添加到您的数据库中。

StudentsLogIn

上面提到的自动迁移步骤仅适用于您的实体。例如,要添加另一个实体类或删除现有实体类,它将成功迁移。但是,如果您向实体类添加或删除任何属性,它将引发异常。

步骤 10 - 要处理属性迁移,您需要在 configuration 类构造函数中设置 AutomaticMigrationDataLossAllowed = true。

public Configuration() {
   AutomaticMigrationsEnabled = true;
   AutomaticMigrationDataLossAllowed = true;
   ContextKey = "EFCodeFirstDemo.MyContext";
}

基于代码的迁移

当您开发新应用程序时,您的数据模型会频繁更改,每次模型更改时,它都会与数据库不同步。您已将实体框架配置为每次更改数据模型时自动删除和重新创建数据库。当您想要更多地控制迁移时,基于代码的迁移非常有用。

  • 当您添加、删除或更改实体类或更改您的 DbContext 类时,下次运行应用程序时,它会自动删除您现有的数据库,创建一个与模型匹配的新数据库,并使用测试数据对其进行填充。

  • 代码优先迁移功能通过启用代码优先更新数据库模式而不是删除和重新创建数据库来解决此问题。要部署应用程序,您必须启用迁移。

以下是迁移数据库更改的基本规则:

  • 启用迁移
  • 添加迁移
  • 更新数据库

让我们看一下基于代码的迁移的分步过程。

当您使用代码优先方法时,您的应用程序没有数据库。

在这个例子中,我们将再次从三个基本类开始,例如学生、课程和注册,如下代码所示。

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

以下是上下文类。

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<
         MyContext, EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}

步骤 1 - 在运行应用程序之前,您需要启用迁移。

步骤 2 - 从工具 → NuGet 程序包管理器 → 程序包管理器控制台打开程序包管理器控制台。

步骤 3 - 迁移已启用,现在通过执行以下命令在您的应用程序中添加迁移。

PM> add-migration "UniDB Schema"

步骤 4 - 命令成功执行后,您将看到在 Migration 文件夹中创建了一个新文件,其名称是您传递给命令的参数加上时间戳前缀,如下图所示。

TimeStamp Prefix

步骤 5 - 您可以使用“update-database”命令创建或更新数据库。

PM> Update-Database -Verbose

“-Verbose”标志指定在控制台中显示应用于目标数据库的 SQL 语句。

步骤 6 - 让我们在学生类中添加另一个属性“年龄”,然后执行更新语句。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public int Age { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

当您执行 PM → Update-Database –Verbose 时,命令成功执行后,您将看到新列 Age 已添加到您的数据库中。

New Column Age.

我们建议您逐步执行上述示例,以便更好地理解。

广告