EF CodeFirst 数据库生成、更新及回滚

说明:
1、本文描述一种使用EF的CodeFirst方式生成、更新及回滚数据库的操作方法,要求:
【1】更新数据库的情况下,希望原有数据库的数据能够尽可能保留;
【2】每次实体模型的变更信息能够保留,并能据此回滚数据库;
2、“补充说明”补充了“操作方法”中涉及的相关知识点的说明;

【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文件夹下自动生成带日期信息的迁移文件(此时数据库还未生成)。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

迁移类 中只有UpDown方法,分别用于对数据库的更新和回滚:
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介绍及使用,参考: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]命令生成其迁移类时,无法更新数据库;
  • (代码迁移)如果要删除某迁移类文件,最好先将数据库回滚到此迁移的上一版本,再删除;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值