• EF Core 的 Code First 模式


    0 前言

    本文正文第一节,会对 Code First 进行基本的介绍,以及对相关名词进行说明,读者一开始可以不用在这里消耗过多时间,可以先操作一遍例子,再回过头理解。

    第二节,以一个简单的例子,展示 EF Core 的 Code First 模式的操作流程。

    第三节,将 Code First 的其他指令例举出来,以便于日后翻查。

    第四节(未完成),将 Code First 其他一些操作,如:在迁移代码中添加 SQL 语句等。

    第五节,将 Code First 模式常见的问题列举出来,防止踩坑。


    1 相关介绍

    1.1 Code First 模式

    以 EF Core 模型为准,使用迁移的方式,将 EF Core 模型的变化以增量的方式更新到数据库。

    简单理解:以C#代码定义的数据实体,生成数据库的表结构。

    1.2 相关名词

    数据库上下文(DbContext):继承自 DbContext,主要作用是连接数据库,跟踪数据实体状态(实体状态包括:added、modified、deleted 等),将数据库实体的状态写入数据库(持久化至数据库中)。

    数据实体(Entity):C#的实体类,与数据库的表对应

    数据模型(Model):暂且认为是数据库的表吧(因为官方文档的描述,感觉就像是)

    约定(conventions):主要是数据实体的类名、属性。

    数据注释(data annotations):应用于类上、属性的特性(如:[Table("SysUser")]),会被 Fluent API 的配置覆盖。

    Fluent API:于自定义的 DbContext 中重写 OnModelCreating 方法中,对数据模型描述的配置,如:

     protected override void OnModelCreating(ModelBuilder modelBuilder)
     {
         builder.Entity<User>().ToTable("SysUser");
     }
    

    数据实体(Entity)、数据模型(Model)、约定(conventions)、数据注释(data annotations)、Fluent API 说明:

    数据实体(Entity)的类名、属性等,称之为约定(conventions),约定主要是为了定义数据模型(Model)的形状。

    但是光靠约定可能不足以完整描述数据模型,有时我们的数据模型与我们的数据实体可能也有差异,这时,就可以通过数据注释(data annotations)和 Fluent API 补充。


    2 EF Core 的基础使用

    2.1 新建 WebApi 工程

    这里基于 VS Code 工具,使用命令行创建一个 WebApi 程序:

    mkdir CodeFirstTest & cd CodeFirstTest #新建文件夹DbFirstTest并切换至该目录下
    dotnet new webapi --framework net6.0   #新建ASP.NET6.0 WebAPI程序
    

    2.2 引入 EF Core 相关 Nuget 包

    EF Core 部分 Nuget 包如下:

    Microsoft.EntityFrameworkCore -->> 核心包
    Microsoft.EntityFrameworkCore.Design -->> Design包:Code First 或 Db First 需要
    Microsoft.EntityFrameworkCore.SqlServer -->> 微软官方 SQL Server 驱动
    Pomelo.EntityFrameworkCore.MySql -->> 社区 MySql 驱动
    MySql.EntityFrameworkCore -->> Oracle官方 MySql 驱动
    

    其中核心包是必须的,另外还需配备对应数据库的驱动包,而 Design 包主要是在使用 Code First 或 Db First 需要的包。

    这里,我们向工程引入必须的 Nuget 包,SQL 驱动程序选择 SQL Server 的:

    dotnet add package Microsoft.EntityFrameworkCore --version 6.0.4
    dotnet add package Microsoft.EntityFrameworkCore.Design --version 6.0.4
    dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 6.0.4
    # dotnet add package Pomelo.EntityFrameworkCore.MySql --version 6.0.1
    

    2.3 准备配置信息

    在 appsettings.json 中增加一个节点,用于连接数据库时使用。

    "ConnectionStrings": {
        "SqlServer": "server=localhost;database=efcore;uid=sa;pwd=Qwe123456;",
        "MySql": "server=localhost;port=3306;database=efcore;user=root;password=123456;charset=utf8mb4;"
    }
    

    2.4 新建数据库上下文 DbContext

    新建一个自定义的数据库上下文 TestDbContext:

    using Microsoft.EntityFrameworkCore;
    
    namespace CodeFirstTest;
    
    public class TestContext : DbContext
    {
        public TestContext(DbContextOptions<TestContext> options) : base(options) { }
    
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            base.OnConfiguring(options);
        }
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<User>();
            base.OnModelCreating(builder);
        }
    
        public virtual DbSet<User> User { get; set; }
    }
    

    2.5 创建数据实体

    创建一个数据实体 User 如下:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.EntityFrameworkCore;
    
    namespace CodeFirstTest;
    
    [Table("SysUser")]
    public class User
    {
        [Key]
        public Guid Id { get; set; } = new Guid();
    
        [StringLength(128)]
        [Comment("姓名")]
        public string? Name { get; set; }
    
        [StringLength(11)]
        [Comment("手机号码")]
        public string? Phone { get; set; }
    }
    

    [Table("SysUser")] 注释数据库中的表名为 SysUser。

    [StringLength(128)] 注释字符串长度,[Comment("姓名")] 注释数据库中该字段含义。

    这些注释,称为“数据注释”,主要是对数据模型的补充描述(可以简单认为:对数据库的表结构的补充描述)。

    关于配置数据模型的详细内容,可以翻查EF Core官方文档:创建并配置模型

    2.6 注册服务

    在 Program.cs 中注册服务,并配置数据库连接串。

    var configuration = builder.Configuration;
    builder.Services.AddDbContext<TestContext>(options => {
        options.UseSqlServer(configuration["ConnectionStrings:SqlServer"]);
    });
    

    2.7 编译项目

    在生成迁移之前,需要先对工程进行编译,否则会报错。

    dotnet build
    

    2.8 数据库迁移(Code First 模式)

    如果没有安装 EF 工具,需要先安装

    # 安装全局工具
    dotnet tool install --global dotnet-ef
    # 更新工具
    dotnet tool update --global dotnet-ef
    

    创建一个名为 Initial 的迁移,将会在项目根目录下生成一个 Migrations 的目录:

    dotnet ef migrations add Initial
    

    更新到数据库

    dotnet ef database update
    

    2.9 增加测试控制器

    增加一个 UserController 用于测试。

    using Microsoft.AspNetCore.Mvc;
    
    namespace CodeFirstTest.Controllers;
    
    [ApiController]
    [Route("[controller]")]
    public class UserController : ControllerBase
    {
        private readonly TestContext _db;
    
        public UserController(TestContext db)
        {
            _db = db;
        }
    
        [HttpGet]
        public User? Get(Guid id)
        {
            return _db.User.Find(id);
        }
    
        [HttpPost]
        public void Post(User user)
        {
            _db.User.Add(user);
            _db.SaveChanges();
        }
    
        [HttpDelete]
        public bool Delete(Guid id)
        {
            User? user = _db.User.Find(id) ?? null;
            if (user == null) return false;
            _db.User.Remove(user);
            _db.SaveChanges();
            return true;
        }
    }
    

    2.10 运行项目

    dotnet build
    dotnet run
    

    访问:https://localhost:7232/swagger/index.html

    对接口进行操作,可以实现对 User 的增删查。

    2.11 源码

    Gitee:https://gitee.com/lisheng741/testnetcore/tree/master/EFCore/CodeFirstTest

    Github:https://github.com/lisheng741/testnetcore/tree/master/EFCore/CodeFirstTest


    3 数据库迁移

    具体参考EF Core官方文档:管理数据库架构:迁移

    3.1 安装工具

    # 安装全局工具
    dotnet tool install --global dotnet-ef
    
    # 更新工具
    dotnet tool update --global dotnet-ef
    
    # 验证安装
    dotnet ef
    

    3.2 迁移

    3.2.1 管理迁移

    创建一个名为 Migrations 的目录,并生成一些文件

    dotnet ef migrations add InitialCreate
    

    创建迁移时指定迁移目录

    dotnet ef migrations add InitialCreate --output-dir [directory]
    

    删除迁移

    dotnet ef migrations remove
    

    列出所有迁移

    dotnet ef migrations list
    

    3.2.2 应用迁移

    应用迁移主要有2种方式,一种是生成 SQL 脚本,一种是通过命令行工具执行命令进行迁移,具体请参考EF Core 官方文档:应用迁移

    除了这两种迁移方式外,还有一种是在程序种迁移,即将迁移的代码写入程序中,由程序运行时触发。

    1) 命令行工具

    将迁移应用到数据库

    dotnet ef database update
    

    将迁移应用到数据库:指定迁移

    dotnet ef database update AddNewTables
    

    注意:使用该命令,也可以进行迁移回滚(回滚到之前的某个迁移)。

    2) 生成 SQL 脚本
    dotnet ef migrations script
    

    指定迁移起点(From)

    dotnet ef migrations script AddNewTables
    

    指定迁移起点(From)和结束点(To)

    dotnet ef migrations script AddNewTables AddAuditTable
    

    幂等 SQL 脚本(idempotent):脚本将在内部检查已经应用哪些迁移(通过迁移历史记录表),并且只应用缺少的迁移。

    dotnet ef migrations script --idempotent
    
    3)在程序运行时进行迁移

    请参考EF Core 文档:应用迁移:在运行时应用迁移


    4 其他操作

    4.1 迁移代码添加 SQL

    请参考:EF Core 官方文档:管理迁移:添加原始 SQL

    下面的例子,用一个新的 FullName 属性替换现有的 FirstNameLastName 属性,并将现存的数据转移到新的列上。

    migrationBuilder.AddColumn<string>(
        name: "FullName",
        table: "Customer",
        nullable: true);
    
    migrationBuilder.Sql("UPDATE Customer SET FullName = FirstName + ' ' + LastName;");
    
    migrationBuilder.DropColumn(
        name: "FirstName",
        table: "Customer");
    
    migrationBuilder.DropColumn(
        name: "LastName",
        table: "Customer");
    

    4.2 自定义迁移操作

    MigrationBuilder.Sql() 或 自定义 MigrationOperation 对象,可以对 MigrationBuilder 进行扩展。

    如:想要在迁移代码中使用如下代码(CreateUser 方法为自定义方法)

    migrationBuilder.CreateUser("SQLUser1", "Password");
    

    4.2.1 使用 MigrationBuilder.Sql()

    CreateUser 自定义代码如下:

    public static OperationBuilder<SqlOperation> CreateUser(
        this MigrationBuilder migrationBuilder,
        string name,
        string password)
        => migrationBuilder.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");
    

    4.2.2 自定义 MigrationOperation 对象

    请参考EF Core 官方文档:自定义操作

    创建和删除 API(EnsureCreated 和 EnsureDeleted)

    在程序运行中可以调用的 API,用于管理数据库的创建和删除。


    5 Code First 模式常见问题

    5.1 列重命名

    具体请查看EF Core 官方文档:管理迁移:列重命名

    官方举的例子:如果你将属性从 Name 重命名为 FullName,EF Core 将生成以下迁移:

    migrationBuilder.DropColumn(
        name: "Name",
        table: "Customers");
    
    migrationBuilder.AddColumn<string>(
        name: "FullName",
        table: "Customers",
        nullable: true);
    

    该迁移代码将 Name 列删除,然后添加新的列 FullName,这样做,会导致 Name 列原有的数据丢失。

    所以需要自行将该迁移代码修改如下:

    migrationBuilder.RenameColumn(
        name: "Name",
        table: "Customers",
        newName: "FullName");
    

    参考来源

    EF Core 官方文档

    EF Core / 基础_从建库到增删改查

    EF Core的基本使用

  • 相关阅读:
    找女朋友的数学期望
    Spring Security认证流程分
    unity基础4-常用插件
    jinfo_动态调整JVM参数(无需重启)(实践)
    【安卓应用渗透】第一篇:安卓逆向回顾和梳理-2211
    安装配置 zookeeper(单机版)
    通用分页(分页代码的封装)
    什么是数字孪生?
    虚拟现实(VR)游戏与增强现实(AR)游戏的区别
    ElasticSearch7.3学习(二十二)----Text字段排序、Scroll分批查询场景解析
  • 原文地址:https://www.cnblogs.com/clis/p/16197076.html