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 数据库迁移
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)在程序运行时进行迁移
4 其他操作
4.1 迁移代码添加 SQL
请参考:EF Core 官方文档:管理迁移:添加原始 SQL
下面的例子,用一个新的 FullName
属性替换现有的 FirstName
和 LastName
属性,并将现存的数据转移到新的列上。
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 对象
创建和删除 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 的 Code First 模式的更多相关文章
- 基于EF Core的Code First模式的DotNetCore快速开发框架
前言 最近接了几个小单子,因为是小单子,项目规模都比较小,业务相对来说,也比较简单.所以在选择架构的时候,考虑到效率方面的因素,就采取了asp.net+entity framework中的code f ...
- 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持
前言 距离上一篇文章<基于EF Core的Code First模式的DotNetCore快速开发框架>已过去大半个年头,时光荏苒,岁月如梭...比较尴尬的是,在这大半个年头里,除了日常带娃 ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- EF Core学习Code First
下面通过实例来学习EF Core Code First,也就是通过EF Core迁移来完成从模型生成数据库. 本实例使用EntityFrameworkCore SQLite 数据库进行介绍,大家也可以 ...
- EF Core的Code First 基础
一.创建实体类与映射类 通过NuGet引用Microsoft.EntityFrameworkCore 1.创建实体类 Code First可以通过为实体类字段添加相应特性,来创建对应的字段类型等,举例 ...
- EF 下的code fist 模式编程
EF 分两种模式 codefirst(就是不知道数据是啥,也没有数据库) 和 database fist (数据已经设计好了) 首先打开vs 新建一个项目 创建一个控制台程序 然后 新建一个Tea ...
- NET Core 使用EF Core的Code First迁移和DBFirst
DBFirst (1)Microsoft.EntityFrameworkCore (2)Microsoft.EntityFrameworkCore.Design (3)Microsoft.Entity ...
- EF core (code first) 通过自定义 Migration History 实现多租户使用同一数据库时更新数据库结构
前言 写这篇文章的原因,其实由于我写EF core 实现多租户的时候,遇到的问题. 具体文章的链接: Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下 ...
- EF core (code first) 通过自动迁移实现多租户数据分离 :按Schema分离数据
前言 本文是多租户系列文章的附加操作文章,如果想查看系列中的其他文章请查看下列文章 主线文章 Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下利用EF ...
随机推荐
- Linux 的目录结构是怎样的?
这个问题,一般不会问.更多是实际使用时,需要知道.Linux 文件系统的结构层次鲜明,就像一棵倒立的树,最顶层是其根目录:Linux的目录结构常见目录说明: /bin:存放二进制可执行文件(ls,ca ...
- 使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的.例如,对于如下语句: final StringBuilder a=new StringBuilder ...
- jpg, jpeg和png区别?
jpg是jpeg的缩写, 二者一致 PNG就是为取代GIF而生的, 无损压缩, 占用内存多 jpg牺牲图片质量, 有损, 占用内存小 PNG格式可编辑.如图片中有字体等,可利用PS再 ...
- Zookeeper 对节点的 watch监听通知是永久的吗?为什么 不是永久的?
不是.官方声明:一个 Watch 事件是一个一次性的触发器,当被设置了 Watch 的数据发生了改变的时候,则服务器将这个改变发送给设置了 Watch 的客户端, 以便通知它们. 为什么不是永久的,举 ...
- Java如何声明变量?JS如何声明变量?
Java采用强类型变量检查,像C语言一样.所有变量在编译之前必须声明,而且不能使用没有赋值的变量.例如:int x;x=1234;char y='F';其中X=1234说明是一个整数,Y='F'说明是 ...
- PowerDesigner生成MySQL脚本,表和字段进行转义
打开Power Designer数据库建模工具,软件基本信息如下 如果PowerDesigner内置的(table_option)表物理操作没有,请看以下步骤 打开 Edit Current DBMS ...
- 使用 Spring 通过什么方式访问 Hibernate?
在 Spring 中有两种方式访问 Hibernate:控制反转 Hibernate Template 和 Callback.继承 HibernateDAOSupport 提供一个 AOP 拦截器.
- 详解 IOC
什么是IOC: IOC-Inversion Of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是 ...
- 学习heartbeat-04 原理及部署
1. Heartbeat介绍 1.1 Heartbeat作用 通过它可以将资源(IP及程序服务等资源)从一台故障计算机快速转移到另一台运转正常的机器继续提供服务,在实际生产应用场景中,heartbea ...
- 2018 百度web前端面试
面试前 正式入职一年半左右,实习半年,勉强两年经验吧,然后很惊喜收到了百度的面试邀约,约得两点钟面试,然后本人一点钟就到了,通电话之后,面试官很热情,说正在吃饭吃完饭就去找我,让我去坐着等一会,然后一 ...