EF-CodeFirst-数据库生成更新及回滚
EF CodeFirst 数据库生成、更新及回滚
说明:
1、本文描述一种使用EF的CodeFirst方式生成、更新及回滚数据库的操作方法,要求:
【1】更新数据库的情况下,希望原有数据库的数据能够尽可能保留;
【2】每次实体模型的变更信息能够保留,并能据此回滚数据库;
2、“ 补充说明 ”补充了“ 操作方法 ”中涉及的相关知识点的说明;
EF CodeFirst 数据库生成、更新及回滚
【1】操作方法
【1.1】生成数据库
1、引入EF框架
在VS中,
工具
->NuGet包管理器
->管理解决方案的NuGet程序包
,打开如下界面,搜索“EntityFramework”,将其安装到需要EF的项目中。2、添加 连接字符串
将连接字符串添加到 启动项目 的App.config文件中,如下所示。
Data Source=.;Initial Catalog=Demo_DB; User Id=sa;Password=123456
// Data Source=. 本地数据库
// Initial Catalog=Demo_DB 数据库名称为Demo_DB
// User Id=sa 使用数据库的sa账户登录数据库
// Password=123456 sa账户密码为123456
- 3、实现DbContext类的派生类
class MyDbContext : DbContext
{
public MyDbContext() : base("name=conStr")
{
// 设置数据库初始化器
}
// 定义实体类的 DbSet
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// 注册Mapping类
}
}
base(“name=conStr”) 代表将name等于"conStr"的连接字符串指向的数据库 作为 目标数据库。
4、生成Configuration类
在“程序包管理器控制台”中输入 enable-migrations 命令,如下图所示。
注意, 默认项目 要选择正确。
命令执行后如下所示,并自动生成 Migrations 文件夹及 Configuration.cs 文件。
5、在DbContext类的派生类添加初始化器
public MyDbContext() : base("name=conStr")
{
// 设置数据库初始化器
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>());
}
初始化器 代表依据实体类生成数据库的策略,MigrateDatabaseToLatestVersion代表在更新数据库时尽可能不删除原有数据库数据的策略。
6、添加实体类及其Mapping类
此处以添加一个Student类为例(对应数据库Student表)。
Student类及StudentMapping类如下:
class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
class StudentMapping : EntityTypeConfiguration<Student>
{
public StudentMapping()
{
// 定义表名
this.ToTable("Student");
// 定义主键
this.HasKey(s => s.Id);
// 定义各属性映射方式
this.Property(s => s.Id).HasColumnName("Id").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
this.Property(s => s.Name).HasColumnName("Name").HasColumnType(SqlDbType.NVarChar.ToString()).HasMaxLength(50).IsRequired();
}
}
StudentMapping类 定义了Student实体类到数据库Student表的映射规则
- 7、在MyDbContext类中注册实体类及其Mapping类
class MyDbContext : DbContext
{
public MyDbContext() : base("name=conStr")
{
// 设置数据库初始化器
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>());
}
// 定义实体类的 DbSet
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// 注册Mapping类
modelBuilder.Configurations.Add(new StudentMapping());
}
}
Students属性 代表整张Student表中的数据。
7、生成迁移类
在“程序包管理器控制台”中使用 add-migration [ name ] 命令,[ name ]为要生成的迁移类名,此处使用“InitialCreate”,如下图。
执行成功后,如下图所示,并在 Migrations 文件夹下自动生成带日期信息的迁移文件(此时数据库还未生成)。
迁移类 中只有 Up 和 Down 方法,分别用于对数据库的更新和回滚:
Up方法 用于更新,将当前数据库更新到本迁移类对应的实体模型的状态;
Down方法 用于回滚,将当前数据库回滚到应用本迁移类之前对应的实体模型的状态;
8、使用迁移类生成数据库
在“程序包管理器控制台”中使用 update-database 命令,如下所示。
执行成功后,如下图所示。
查看数据库,Student数据表已生成。
【1.2】更新数据库
举例说明: Student表原有两条数据如下图所示,向Student实体类增加一个Age字段后,更新数据库。
- 1、修改实体类及Mapping文件
class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
class StudentMapping : EntityTypeConfiguration<Student>
{
public StudentMapping()
{
// 定义表名
this.ToTable("Student");
// 定义主键
this.HasKey(s => s.Id);
// 定义各属性映射方式
this.Property(s => s.Id).HasColumnName("Id").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).IsRequired();
this.Property(s => s.Name).HasColumnName("Name").HasColumnType(SqlDbType.NVarChar.ToString()).HasMaxLength(50).IsRequired();
this.Property(s => s.Age).HasColumnName("Age").HasColumnType(SqlDbType.Int.ToString()).IsRequired();
}
}
2、生成迁移类
在“程序包管理器控制台”中使用 add-migration [ name ] 命令,生成新的迁移类,如下所示。
3、使用迁移类更新数据库
在“程序包管理器控制台”中使用 update-database 命令,如下所示。
查看数据库,Student表已更新,且原有数据仍在。
【1.3】回滚数据库
举例说明: 希望将数据库回滚到 InitialCreate迁移类 对应的实体模型状态时期的版本。
在“程序包管理器控制台”中使用 update-database -TargetMigration: [name] 命令,如下所示。
执行成功,如下图所示。
数据库也退回之前的表结构,且数据保留。
【2】补充说明
【2.1】EF框架
NuGet介绍及使用,参考:
EF框架安装后引入的程序集:
本文内容仅使用到了EntityFramework.dll。
【2.2】数据库连接配置
在 【1.1】生成数据库 中,步骤2、3为配置 数据库连接 的过程,包括此方法在内,有如下几种方式:
1. 连接字符串name与DbContext类的派生类同名
2. 在DbContext类的派生类的构造函数中传入连接字符串的name (本文方法)
3. 在DbContext类的派生类的构造函数中传入数据库名
此种方法不使用连接字符串;
似乎只能在VS的Local SQL Server中生成数据库;
暂不介绍;
【2.3】数据库初始化器
在 【1.1】生成数据库 中,步骤5为添加 数据库初始化器 的方法。
内置4种初始化器,如下:
1.CreateDatabaseIfNotExists (EF默认)
特点 :
1)数据库不存在,创建数据库;
2)数据库存在,不创建数据库;
3)存在的数据库与实体类模型不符,抛出异常;
使用 :
Database.SetInitializer(new CreateDatabaseIfNotExists<MyDbContext>());
2.DropCreateDatabaseIfModelChanges
特点 :
1)当实体类模型改变时,删除原有数据库,重新创建数据库;
使用 :
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyDbContext>());
3.DropCreateDatabaseAlways
特点 :
1)每次运行都删除原有数据库,重新创建数据库;
使用 :
Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());
4.MigrateDatabaseToLatestVersion (本文使用的)
1、2、3中的初始化器,不需要 【1.1】生成数据库 步骤4中生成的 Configuration 类(也可以使用);
所以,也不能使用 update-database 命令生成数据库;
要生成数据库,可以运行程序,执行到 创建DbContext派生类对象时 即会生成;
2、3中的初始化器,因为要删除原有数据库,所以可能会抛出“无法删除数据库,因为数据库正在使用的异常”,这多半是SQL Server Management Studio连接了数据库并进行了一些操作导致的,断开连接即可;
- 自定义初始化器:可以继承上述初始化器,创建自定义初始化器,重写Seed方法添加初始数据等。
【2.4】自动迁移和代码迁移
在 【1.1】生成数据库 中,步骤4、5为配置 数据库迁移 的方法,本文使用的是 代码迁移 ,还有一种方式叫 自动迁移 。
两种迁移均使用 MigrateDatabaseToLatestVersion 数据库初始化器。
介绍两种迁移:
1. 自动迁移
在“程序包管理器控制台”中输入 enable-migrations -EnableAutomaticMigration:$true 命令,生成Configuration类如下:
在“程序包管理器控制台”中输入 update-database 命令,执行后如下图所示,并且不会生成代码迁移中带日期信息的迁移类。
1. 代码迁移
如 【1.1】生成数据库 中,步骤4、5所述。
【2.5】__MigrationHistory表
__MigrationHistory表是EF创建数据库时自动生成的表;
该表中存储当前数据库对应的实体模型版本的相关信息,例如下图:
- MigrationId
- 带时间戳的实体模型版本名;
- ContextKey
- 实体类模型的组名,一个组对应一个项目(仅用于多个项目共享一个数据库的情况);
- Model
- 此版本实体类模型的二进制信息;
- ProductVersion
- EF的版本号;
ContextKey 可以在Configuration类中手动指定,如下图。
【2.6】自动迁移的更新及回滚
更新
在 【2.4】 中介绍的 自动迁移 ,生成数据库没有问题,但是更新存在问题。
只是添加实体类的更新也没有问题,但如果要修改原有实体类的属性时,就会抛出异常,此时可以在 Configuration类 加入 AutomaticMigrationDataLossAllowed = true 解决此问题,如下图。
AutomaticMigrationDataLossAllowed = true 代表允许迁移的时候产生数据丢失。
回滚
自动迁移 也可以实现回滚操作:
在数据库的__MigrationHistory表中找到要回滚到的版本,如下图选中项。
在“程序包管理器控制台”中使用 update-database -TargetMigration: [name] 命令,其中 [name] 要使用带时间戳的全名,如下图所示。
执行成功,如下图所示。
数据库的__MigrationHistory表也表明退回了版本,如下。
【2.7】三个命令
enable-migrations
- 用于自动迁移或代码迁移时生成Configuration类;
- 通过 “-EnableAutomaticMigration:$true” 参数控制是自动迁移还是代码迁移;
自动迁移
代码迁移
- 如果已存在的目标数据库中__MigrationHistory表的ContextKey与enable-migrations命令默认的ContextKey不相同,则会在Configuration类的构造函数中生成ContextKey,同时生成当前数据库实体类模型的迁移类文件;
add-migration [name]
- 用于生成当前实体类模型的迁移类文件;
- (代码迁移)如果当前最新的迁移类文件,还没有通过update-database命令更新到数据库,则不能通过add-migration [name]命令生成更新的迁移类文件;
update-database
- 用于按照迁移策略更新数据库;
- (代码迁移)最新的实体类模型在未使用add-migration [name]命令生成其迁移类时,无法更新数据库;
- (代码迁移)如果要删除某迁移类文件,最好先将数据库回滚到此迁移的上一版本,再删除;