本系列专栏基于杨中科老师的《ASP.NET Core技术内幕与项目实战》,本人记录梳理的学习笔记,有部分的增补和省略。更全面系统的讲解,请看杨老师的视频课:【.NET教程,.Net Core视频教程,杨中科主讲】。
一、基本概念
1. 定义
Entity Framework Core(简称 EF Core)是 .NET 平台下轻量级、跨平台、开源的对象关系映射(ORM)框架,由 Microsoft 官方开发和维护。它的核心价值是实现「对象」与「关系型数据库」的解耦:开发者只需操作 .NET 实体对象,EF Core 会自动将对象操作转换为对应的 SQL 语句,无需手动编写大量原生 SQL。
EF Core 支持主流数据库(SQL Server、MySQL、PostgreSQL、SQLite、Oracle 等),是 .NET 生态中最主流的 ORM 工具,尤其与 ASP.NET Core 深度集成。
核心优势:
- 提高开发效率:消除数据访问层的样板代码,专注业务逻辑开发
- 强类型查询:基于 LINQ 的查询语法,编译期即可发现语法错误(对比字符串拼接 SQL 的运行时错误)
- 数据库迁移:自动化管理数据库结构变更(新增字段、修改类型、删除表等),支持回滚
- 跨平台兼容:可运行在 Windows、Linux、macOS 等系统,适配 .NET Core/.NET 5+ 全版本
- 生态深度集成:原生支持依赖注入、异步编程、ASP.NET Core 等 .NET 核心特性
- 变更追踪:自动跟踪实体对象的状态变化(新增 / 修改 / 删除),无需手动标记
2. 与主流 ORM 框架对比
| ORM 框架 | 定位 | 核心优势 | 核心劣势 | 适用场景 |
|---|---|---|---|---|
| EF Core | 全功能官方 ORM | LINQ 支持、自动迁移、变更追踪、多数据库兼容 | 复杂查询性能略逊于微 ORM、学习成本稍高 | 企业级项目、CRUD 逻辑复杂、架构清晰的场景 |
| Dapper | 轻量级微 ORM | 性能接近原生、ADO.NET、完全控制 SQL、体积小 | 无自动迁移、无变更追踪、需手动映射关联 | 高并发、性能敏感、需精准控制 SQL 的场景 |
| SqlSugar/FreeSql | 国内开源轻量 ORM | 语法简洁、上手快、文档友好、功能全面 | 生态不如官方完善、大型项目验证度稍低 | 中小型项目、快速开发、多数据库兼容场景 |
补充说明:
- EF Core 并非「性能差」,而是「封装度高」:简单 CRUD 性能与 Dapper 差距极小,仅在超复杂查询 / 海量数据操作时需手动优化(如原生 SQL 拼接、索引优化);
- SqlSugar/FreeSql 是国内社区的优秀产物,兼顾 EF Core 的易用性和 Dapper 的轻量,适合中小团队快速落地。
二、EF Core 基础使用方法
Code First 是 EF Core 最常用的开发模式:先编写 .NET 实体类,再通过 EF Core 自动生成数据库表结构。
1. 环境准备(安装依赖包)
根据目标数据库选择对应的 NuGet 包,以 SQL Server 为例,在命令行:
# 核心包:EF Core 核心功能 + SQL Server 驱动
Install-Package Microsoft.EntityFrameworkCore.SqlServer
# 工具包:支持迁移、数据库更新等命令
Install-Package Microsoft.EntityFrameworkCore.Tools
# 可选:可视化设计器(按需安装)
Install-Package Microsoft.EntityFrameworkCore.Design
2. 编写实体类(Entity)
实体类对应数据库中的表,属性对应表的字段,EF Core 遵循「约定大于配置」原则(如 Id 会自动识别为主键)。
// 书籍实体
public class Book
{
// 主键(约定:Id 或 BookId 会被自动识别为主键)
public long Id { get; set; }
// 书籍标题
public string Title { get; set; }
// 发布时间
public DateTime PubTime { get; set; }
// 价格
public double Price { get; set; }
// 可选字段(演示可空属性)
public string? AuthorName { get; set; }
}
// 人员实体
public class Person
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
// 新增字段(演示迁移场景)
public DateTime? BirthDate { get; set; }
}
3. 编写实体配置类(可选,精细化配置)
当需要打破「约定」时(如自定义表名、字段长度、非空约束),通过 IEntityTypeConfiguration<T> 配置实体映射规则,相比特性(Attribute)更易维护。
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
// 书籍实体配置
public class BookEntityConfig : IEntityTypeConfiguration<Book>
{
public void Configure(EntityTypeBuilder<Book> builder)
{
// 自定义表名(默认表名是实体类名复数:Books)
builder.ToTable("T_Books");
// 配置字段约束:标题最大长度50,非空
builder.Property(e => e.Title)
.HasMaxLength(50)
.IsRequired();
// 配置价格精度(避免浮点数精度问题)
builder.Property(e => e.Price)
.HasColumnType("decimal(18,2)");
}
}
// 人员实体配置
public class PersonEntityConfig : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.ToTable("T_Persons");
// 姓名非空,最大长度20
builder.Property(e => e.Name)
.HasMaxLength(20)
.IsRequired();
// 年龄范围限制
builder.Property(e => e.Age)
.HasCheckConstraint("CK_Person_Age", "Age >= 0 AND Age <= 150");
}
}
4. 编写 DbContext 类(核心:数据库上下文)
DbContext 是 EF Core 的核心类,相当于「数据库会话」:管理实体与数据库的映射、数据库连接、变更追踪、查询执行等。
using Microsoft.EntityFrameworkCore;
public class TestDbContext : DbContext
{
// 声明 DbSet<T>:表示数据库中的对应表(EF Core 据此识别实体)
public DbSet<Book> Books { get; set; }
public DbSet<Person> Persons { get; set; }
// 配置数据库连接
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
// SQL Server 连接字符串(根据自己的数据库地址修改)
string connStr = "Server=.;Database=EFTestDemo;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True";
// 指定使用 SQL Server 数据库
optionsBuilder.UseSqlServer(connStr);
// 可选:开发环境打印生成的 SQL(调试用)
// optionsBuilder.LogTo(Console.WriteLine);
}
// 加载实体配置类
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// 自动加载当前程序集中所有实现 IEntityTypeConfiguration<T> 的配置类
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
5. 生成 / 更新数据库(迁移命令)
EF Core 的「迁移(Migration)」是管理数据库结构变更的核心功能,所有表结构修改都通过迁移命令同步到数据库。
第一步:初始化迁移(首次生成数据库)
打开「Visual Studio → 工具 → NuGet 包管理器 → 程序包管理器控制台(PMC)」,执行:
# 创建迁移文件(InitialCreate 是迁移名称,建议语义化命名)
Add-Migration InitialCreate
# 将迁移应用到数据库(自动创建数据库 + 表)
Update-Database
第二步:修改表结构(新增字段 / 修改约束)
- 先修改实体类 / 配置类(如给
Person新增BirthDate字段); - 执行迁移命令,在命令行:
# 创建新的迁移文件(记录结构变更)
Add-Migration AddPersonBirthDate
# 同步到数据库
Update-Database
迁移常用补充命令:
在命令行:
# 查看迁移历史
Get-Migration
# 回滚上一次迁移
Update-Database -Migration InitialCreate
# 删除最近一次未应用的迁移文件
Remove-Migration
6. 核心 CRUD 操作(业务代码)
EF Core 支持同步 / 异步两种操作方式,推荐优先使用异步(避免阻塞线程),所有操作需通过 SaveChanges/SaveChangesAsync 提交到数据库。
using System.Linq;
using System.Threading.Tasks;
class EFCRUDExample
{
// 新增数据
public static async Task AddData()
{
// 使用 using 自动释放 DbContext 资源
using (var ctx = new TestDbContext())
{
var book = new Book
{
Title = "数学之美",
PubTime = new DateTime(2020, 1, 1),
Price = 49.9,
AuthorName = "吴军"
};
// 将实体加入上下文(标记为新增状态)
ctx.Books.Add(book);
// 提交变更(生成 INSERT 语句)
await ctx.SaveChangesAsync();
Console.WriteLine($"新增书籍 ID:{book.Id}");
}
}
// 删除数据
public static async Task DeleteData()
{
using (var ctx = new TestDbContext())
{
// 先查询要删除的实体
var book = await ctx.Books.SingleOrDefaultAsync(b => b.Title == "数学之美");
if (book != null)
{
// 标记为删除状态
ctx.Books.Remove(book);
// 提交变更(生成 DELETE 语句)
await ctx.SaveChangesAsync();
Console.WriteLine("删除成功");
}
}
}
// 修改数据
public static async Task UpdateData()
{
using (var ctx = new TestDbContext())
{
// 先查询实体
var book = await ctx.Books.SingleOrDefaultAsync(b => b.Title == "数学之美");
if (book != null)
{
// 直接修改属性(EF Core 自动追踪变更)
book.Price = 59.9;
book.AuthorName = "Jun Wu";
// 提交变更(生成 UPDATE 语句)
await ctx.SaveChangesAsync();
Console.WriteLine("修改成功");
}
}
}
// 查询数据(LINQ 语法)
public static async Task QueryData()
{
using (var ctx = new TestDbContext())
{
// 1. 基础查询:价格大于80的书籍
var expensiveBooks = ctx.Books.Where(b => b.Price > 80);
// 2. 排序:按价格升序
var sortedBooks = ctx.Books.OrderBy(b => b.Price);
// 3. 分组:按作者分组,统计书籍数量和最高价格
var authorGroup = ctx.Books
.GroupBy(b => b.AuthorName)
.Select(g => new
{
AuthorName = g.Key,
BookCount = g.Count(),
MaxPrice = g.Max(b => b.Price)
});
// 4. 单条查询:Single(无结果/多结果会抛异常)
var singleBook = await ctx.Books.SingleAsync(b => b.Title == "零基础趣学C语言");
// 5. 安全查询:FirstOrDefault(无结果返回 null)
var firstBook = await ctx.Books.FirstOrDefaultAsync(b => b.Id == 9);
// 遍历结果(延迟执行:只有遍历/ToList 时才生成 SQL 执行)
foreach (var book in expensiveBooks)
{
Console.WriteLine($"{book.Title} - {book.Price}元");
}
}
}
// 批量操作(补充:原生 EF Core 无批量操作,需第三方库)
public static async Task BatchOperation()
{
// 推荐使用 Zack.EFCore.Batch 库(NuGet 安装:Zack.EFCore.Batch)
using (var ctx = new TestDbContext())
{
// 批量删除:价格小于50的书籍
await ctx.Books.Where(b => b.Price < 50).BatchDeleteAsync();
// 批量更新:所有书籍价格涨10元
await ctx.Books.BatchUpdateAsync(b => new Book { Price = b.Price + 10 });
}
}
}
7. 关键注意事项
DbContext是轻量级对象,每次操作应创建新实例(使用 using 自动释放),不要单例复用;- LINQ 查询是「延迟执行」:只有调用
ToListAsync()/FirstAsync()/ 遍历等操作时,才会生成 SQL 并执行; - 异步方法优先:所有 CRUD 操作都有
Async后缀的异步版本(如SaveChangesAsync),避免阻塞主线程; - 连接字符串配置:生产环境建议将连接字符串写入
appsettings.json,而非硬编码(通过依赖注入读取)。
总结
- EF Core 是 .NET 官方全功能 ORM,核心价值是通过对象操作数据库,支持 LINQ、自动迁移、变更追踪等特性;
- Code First 模式的核心步骤:安装依赖 → 写实体类 → 配置 DbContext → 迁移生成数据库 → 编写 CRUD 代码;
- 迁移是 EF Core 管理数据库结构的核心方式,语义化命名迁移文件、优先使用异步操作是最佳实践。


被折叠的 条评论
为什么被折叠?



