7.1 ORM 概念与 EF Core 架构
ORM(Object-Relational Mapping):将对象模型与关系数据库映射的技术,核心优势:
- 用面向对象语法操作数据库(无需手写 SQL);
- 屏蔽不同数据库差异(如 SQL Server/MySQL,代码无需修改);
- 简化数据访问代码,提升开发效率。
EF Core(Entity Framework Core):.NET 跨平台的 ORM 框架,支持.NET 5+特点:
- 轻量级、高性能;
- 支持代码优先(Code-First)、数据库优先(Database-First)两种开发模式;
- 可扩展(支持自定义插件,如批量操作、日志)。
EF Core 核心组件:
- DbContext:数据库上下文,管理实体与数据库的交互(如跟踪实体状态、执行查询、保存更改);
- DbSet:表明数据库中的表,用于 CRUD 操作(如DbSet<User>对应 Users 表);
- 实体(Entity):映射到数据库表的类(如 User 类对应 Users 表,类的属性对应表的字段);
- 模型构建器(ModelBuilder):用于配置实体与表的映射关系(如主键、外键、字段类型);
- 数据库提供程序:适配不同数据库(如Microsoft.EntityFrameworkCore.SqlServer、Pomelo.EntityFrameworkCore.MySql)。
7.2 数据模型设计与实体配置
实体设计原则:
1)每个实体对应一张表,类名提议与表名一致(或通过配置指定);
2)主键属性提议命名为Id或[实体名]Id(如UserId),EF Core 默认识别;
3)属性类型与数据库字段类型匹配(如string对应nvarchar,int对应int)。
示例:基础实体类:
// 用户实体(对应Users表)
public class User
{
public int Id { get; set; } // 主键(EF Core默认识别)
public string Name { get; set; } // 对应Name字段(nvarchar)
public int Age { get; set; } // 对应Age字段(int)
public decimal Balance { get; set; } // 对应Balance字段(decimal)
public DateTime CreateTime { get; set; } // 对应CreateTime字段(datetime2)
// 导航属性:表明与Order的一对多关系(一个用户有多个订单)
public List<Order> Orders { get; set; } = new List<Order>();
}
// 订单实体(对应Orders表)
public class Order
{
public int Id { get; set; }
public string OrderNo { get; set; } // 订单编号
public decimal TotalAmount { get; set; } // 订单总金额
public int UserId { get; set; } // 外键(关联User.Id)
// 导航属性:表明与User的多对一关系(一个订单属于一个用户)
public User User { get; set; }
}
实体配置方式:数据注解(Data Annotations):直接在实体属性上添加特性,简单直观,示例:
public class User
{
[Key] // 指定为主键(若命名不符合默认规则时使用)
public int UserId { get; set; }
[Required] // 非空(对应数据库字段NOT NULL)
[MaxLength(50)] // 最大长度50(对应nvarchar(50))
public string Name { get; set; }
[Column("UserAge")] // 指定数据库字段名为UserAge(而非Age)
public int Age { get; set; }
[NotMapped] // 不映射到数据库字段(仅内存中使用)
public string FullName => $"用户-{Name}";
}
Fluent API:在DbContext的OnModelCreating方法中配置,支持更复杂的映射(如多对多关系、索引),示例:
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置User实体
modelBuilder.Entity<User>(entity =>
{
entity.ToTable("Users"); // 指定表名(若类名与表名不同)
entity.HasKey(u => u.Id); // 指定主键
// 配置Name字段:非空、最大长度50、索引
entity.Property(u => u.Name)
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
// 配置CreateTime:默认值为当前时间
entity.Property(u => u.CreateTime)
.HasDefaultValueSql("GETDATE()");
// 配置与Order的一对多关系(一个User对应多个Order)
entity.HasMany(u => u.Orders)
.WithOne(o => o.User)
.HasForeignKey(o => o.UserId) // 指定外键
.OnDelete(DeleteBehavior.Cascade); // 级联删除(删除User时删除关联的Order)
});
// 配置Order实体
modelBuilder.Entity<Order>(entity =>
{
entity.Property(o => o.OrderNo)
.IsRequired()
.HasMaxLength(20);
// 为OrderNo创建唯一索引(避免重复订单号)
entity.HasIndex(o => o.OrderNo).IsUnique();
});
}
}
7.3 DbContext 生命周期与配置
DbContext 生命周期:创建→使用→释放,需遵循 “短生命周期” 原则(避免长时间占用数据库连接),推荐用using语句管理。
DbContext 核心作用:
- 跟踪实体状态:实体有 4 种状态:Added(新增)、Modified(修改)、Deleted(删除)、Unchanged(未变更);
- 执行查询:通过DbSet<T>或Set<T>()发起查询(最终转换为 SQL);
- 保存更改:调用SaveChanges()/SaveChangesAsync(),将实体状态变更同步到数据库。
DbContext 配置(连接字符串):
方式 1:重写 OnConfiguring 方法(简单场景):
public class AppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 配置SQL Server连接字符串
string connectionString = "Server=localhost;Database=MyDB;User ID=sa;Password=123456;TrustServerCertificate=True;";
optionsBuilder.UseSqlServer(connectionString)
.LogTo(Console.WriteLine); // 日志输出到控制台(便于调试SQL)
}
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
}
方式 2:依赖注入配置(ASP.NET Core/Web API 场景,推荐):
- 第一步:安装 NuGet 包Microsoft.EntityFrameworkCore.SqlServer;
- 第二步:在Program.cs中注册 DbContext:
var builder = WebApplication.CreateBuilder(args);
// 注册AppDbContext,从配置文件读取连接字符串
builder.Services.AddDbContext<AppDbContext> (options =>
{
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
options.UseSqlServer(connectionString);
});
var app = builder.Build();
第三步:在appsettings.json中配置连接字符串:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyDB;User ID=sa;Password=123456;TrustServerCertificate=True;"
}
}
DbContext 使用示例(ASP.NET Core 控制器):
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly AppDbContext _dbContext;
// 构造函数注入AppDbContext(依赖注入自动管理生命周期)
public UserController(AppDbContext dbContext)
{
_dbContext = dbContext;
}
// 查询所有用户
[HttpGet]
public async Task<ActionResult<List<User>>> GetAllUsers()
{
var users = await _dbContext.Users.ToListAsync();
return Ok(users);
}
// 新增用户
[HttpPost]
public async Task<ActionResult<User>> AddUser(User user)
{
user.CreateTime = DateTime.Now;
_dbContext.Users.Add(user); // 标记实体为Added状态
await _dbContext.SaveChangesAsync(); // 同步到数据库,生成INSERT语句
return CreatedAtAction(nameof(GetAllUsers), new { id = user.Id }, user);
}
}
7.4 数据库迁移与版本控制
数据库迁移(Migration):EF Core 通过迁移自动生成 / 更新数据库结构(基于实体模型),解决 “实体变更后手动修改数据库” 的问题,支持版本控制(可回滚到历史版本)。
迁移核心命令(通过 Package Manager Console 或.NET CLI):
|
命令(.NET CLI) |
作用 |
|
dotnet ef migrations add 迁移名 |
创建新迁移(对比当前模型与历史模型) |
|
dotnet ef database update |
应用所有未应用的迁移到数据库 |
|
dotnet ef database update 迁移名 |
回滚到指定迁移版本 |
|
dotnet ef migrations remove |
删除最近创建的未应用迁移 |
|
dotnet ef migrations list |
列出所有迁移及其状态(已应用 / 未应用) |
迁移操作步骤:
- 初始化迁移:
- 安装 EF Core 工具:dotnet tool install –global dotnet-ef;
- 创建第一个迁移:dotnet ef migrations add InitialCreate(生成迁移文件,包含创建表的 SQL 逻辑);
- 应用迁移到数据库:dotnet ef database update(执行迁移文件,创建 Users、Orders 表)。
- 实体变更后更新迁移:
- 例如:给 User 类添加Email属性:public string Email { get; set; };
- 创建新迁移:dotnet ef migrations add AddEmailToUser;
- 应用迁移:dotnet ef database update(自动生成 ALTER TABLE 语句,添加 Email 字段)。
- 回滚迁移:
- 回滚到 InitialCreate 版本:dotnet ef database update InitialCreate(删除 AddEmailToUser 迁移的变更,即删除 Email 字段);
- (可选)删除回滚后的迁移文件:dotnet ef migrations remove。
迁移文件解析:
- 每个迁移包含两个文件:
- [迁移ID]_迁移名.cs(Up 方法:应用迁移的逻辑;Down 方法:回滚迁移的逻辑);
- [迁移ID]_迁移名.Designer.cs(迁移元数据)。
- 示例:AddEmailToUser 迁移的 Up 方法:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Email",
table: "Users",
type: "nvarchar(max)",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Email",
table: "Users");
}
- 注意事项:
1)迁移文件需纳入版本控制(如 Git),确保团队成员使用一样的数据库结构;
2)生产环境迁移需谨慎,提议先在测试环境验证;
3)若手动修改了数据库结构,需确保与迁移文件一致,否则会导致迁移失败。



收藏了,感谢分享