-
EF Core入门:ORM映射与查询
EF Core入门:ORM映射与查询
概述
EF Core(Entity Framework Core)是.NET生态中最流行的ORM框架(对象关系映射),它将C#实体类与数据库表关联,让开发者用面向对象的方式操作数据库,无需手写大量SQL。核心优势包括:
消除手写SQL的繁琐,降低出错概率;
支持多种数据库(SQL Server、MySQL、PostgreSQL等);
内置LINQ查询,无缝集成C#语法;
自动管理数据库连接与事务。
本节通过完整实例讲解EF Core的核心用法:实体映射、DbContext配置、CRUDvb.net教程C#教程python教程SQL教程access 2010教程
操作、LINQ查询,重点覆盖映射规则和查询优化。
-
实例代码:完整的ORM映射与查询流程
以下实例定义User和Order两个实体(一对多关系),通过EF Core完成数据库创建、数据增删改查、关联查询:
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; // EF Core核心包(需NuGet安装)
// -------------------------- 1. 定义实体类 --------------------------
public class User
{
// 主键(自增)
public int Id { get; set; }
// 用户名(非空,最大长度50)
public string Name { get; set; }
// 邮箱(唯一,最大长度100)
public string Email { get; set; }
// 导航属性:一个用户有多个订单
public List<Order> Orders { get; set; } = new List<Order>(); // 初始化避免null
}
public class Order
{
// 主键(自增)
public int Id { get; set; }
// 订单号(非空,最大长度20)
public string OrderNumber { get; set; }
// 订单日期(默认当前时间)
public DateTime OrderDate { get; set; }
// 订单金额
public decimal TotalAmount { get; set; }
// 外键:关联User表
public int UserId { get; set; }
// 导航属性:一个订单属于一个用户
public User User { get; set; }
}
// -------------------------- 2. 配置DbContext(数据库会话) --------------------------
public class AppDbContext : DbContext
{
// 对应数据库表:Users、Orders
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
// 配置数据库连接(开发环境,生产环境用配置文件)
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer("Server=localhost;Database=EFCoreDemo;User Id=sa;Password=YourPassword;TrustServerCertificate=True;")
.LogTo(Console.WriteLine); // 输出生成的SQL(调试用)
}
// 配置实体映射(Fluent API)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置User实体
modelBuilder.Entity<User>()
.HasKey(u => u.Id) // 主键
.HasName("PK_Users"); // 主键约束名
modelBuilder.Entity<User>()
.Property(u => u.Name)
.IsRequired() // 非空
.HasMaxLength(50) // 最大长度50
.HasColumnType("nvarchar(50)"); // 列类型
modelBuilder.Entity<User>()
.Property(u => u.Email)
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)")
.IsUnicode(false); // 非Unicode(节省空间)
// 配置Order实体
modelBuilder.Entity<Order>()
.HasKey(o => o.Id);
modelBuilder.Entity<Order>()
.Property(o => o.OrderNumber)
.IsRequired()
.HasMaxLength(20);
modelBuilder.Entity<Order>()
.Property(o => o.OrderDate)
.HasDefaultValueSql("GETDATE()"); // 默认值:当前时间
modelBuilder.Entity<Order>()
.Property(o => o.TotalAmount)
.HasColumnType("decimal(18,2)"); // 精度18,小数位2
// 配置一对多关系:User → Orders
modelBuilder.Entity<Order>()
.HasOne(o => o.User) // 订单属于一个用户
.WithMany(u => u.Orders) // 用户有多个订单
.HasForeignKey(o => o.UserId) // 外键
.HasConstraintName("FK_Orders_Users_UserId") // 外键约束名
.OnDelete(DeleteBehavior.Cascade); // 删除用户时级联删除订单
}
}
// -------------------------- 3. 执行CRUD与查询 --------------------------
class EFCoreExample
{
static async Task Main(string[] args)
{
// 实例化DbContext(轻量级,每次操作创建新实例)
using var dbContext = new AppDbContext();
// 确保数据库已创建(开发环境用,生产环境用迁移)
await dbContext.Database.EnsureCreatedAsync();
// -------------------------- 1. 添加用户与订单 --------------------------
var newUser = new User
{
Name = "张三",
Email = "zhangsan@example.com",
Orders = new List<Order>
{
new Order { OrderNumber = "ORD2024001", TotalAmount = 199.99m },
new Order { OrderNumber = "ORD2024002", TotalAmount = 299.99m }
}
};
dbContext.Users.Add(newUser);
var affectedRows = await dbContext.SaveChangesAsync();
Console.WriteLine($"添加了{affectedRows}条记录");
// -------------------------- 2. 查询用户及其订单(立即加载) --------------------------
var userWithOrders = await dbContext.Users
.Include(u => u.Orders) // 加载导航属性(避免N+1查询)
.FirstOrDefaultAsync(u => u.Name == "张三");
if (userWithOrders != null)
{
Console.WriteLine($"\n用户:{userWithOrders.Name},订单数:{userWithOrders.Orders.Count}");
foreach (var order in userWithOrders.Orders)
{
Console.WriteLine($"订单号:{order.OrderNumber},金额:{order.TotalAmount:C}");
}
}
// -------------------------- 3. 更新用户信息 --------------------------
if (userWithOrders != null)
{
userWithOrders.Email = "zhangsan_new@example.com";
affectedRows = await dbContext.SaveChangesAsync();
Console.WriteLine($"\n更新了{affectedRows}条记录");
}
// -------------------------- 4. 删除订单 --------------------------
var orderToDelete = await dbContext.Orders
.FirstOrDefaultAsync(o => o.OrderNumber == "ORD2024001");
if (orderToDelete != null)
{
dbContext.Orders.Remove(orderToDelete);
affectedRows = await dbContext.SaveChangesAsync();
Console.WriteLine($"\n删除了{affectedRows}条记录");
}
// -------------------------- 5. 非跟踪查询(优化性能) --------------------------
var usersNoTracking = await dbContext.Users
.AsNoTracking() // 不跟踪实体(只读场景用)
.Where(u => u.Email.Contains("example.com"))
.ToListAsync();
Console.WriteLine($"\n非跟踪查询到{usersNoTracking.Count}个用户");
}
}
-
逐行讲解
2.1 实体类定义
主键:每个实体必须有主键(EF Core默认识别Id或UserId为键);
导航属性:User.Orders和Order.User表示一对多关系,需初始化(如new List())避免null;
外键:Order.UserId关联User.Id,是关系的物理体现。
2.2 DbContext配置
DbContext:数据库会话的核心,负责实体与数据库的交互;
DbSet:对应数据库表(如DbSet→Users表);
OnConfiguring:配置数据库连接(生产环境应从配置文件读取连接字符串);
OnModelCreating:用Fluent API配置映射(比数据注解更灵活),如主键、列类型、关系约束。
2.3 CRUD操作
添加:Add/AddRange添加实体,SaveChangesAsync提交事务;
查询:用LINQ方法(FirstOrDefaultAsync、Where)查询,Include加载导航属性;
更新:修改跟踪实体的属性后,SaveChangesAsync自动生成UPDATE语句;
删除:Remove/RemoveRange标记实体为删除,SaveChangesAsync执行DELETE。
2.4 关键优化点
立即加载:Include避免N+1查询(一次JOIN查询获取所有数据);
非跟踪查询:AsNoTracking不缓存实体,适合只读场景,内存占用更低;
异步操作:Async后缀方法(如SaveChangesAsync)提高并发性能。 -
基础知识拓展
3.1 映射方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 数据注解 | 简单直观,代码与映射同处实体类 | 灵活性低,复杂映射支持不足 | 简单实体(如单表 |
| Fluent API | 灵活强大,支持所有映射规则 | 代码分散在OnModelCreating | 复杂实体(如关系、索引) |
示例:数据注解映射
csharp
public class User
{
[Key(Name = "PK_Users")]
public int Id { get; set; }
[Required, MaxLength(50), Column(TypeName = "nvarchar(50)")]
public string Name { get; set; }
[Required, MaxLength(100), Column(TypeName = "varchar(100)", IsUnicode = false)]
public string Email { get; set; }
}
3.2 EF Core生命周期
DbContext实例:轻量级,应短期持有(如ASP.NET Core中用Scoped生命周期);
跟踪机制:EF Core默认跟踪实体状态(Added/Modified/Deleted),修改后自动同步到数据库;
状态管理:用Entry手动修改实体状态(如dbContext.Entry(user).State = EntityState.Modified)。
3.3 常见查询模式
| 模式 | 用途 | 示例 |
|---|---|---|
| 延迟加载 | 按需加载导航属性(需安装Proxies包) | user.Orders自动触发查询 |
| 投影查询 | 只返回需要的字段(减少数据传输) | Select(u => new { u.Name, u.Email }) |
| 分组查询 | 按字段分组统计 | GroupBy(u => u.Name).Select(g => new { Name = g.Key, Count = g.Count() }) |
3.4 迁移(Migration)
EF Core用迁移管理数据库schema变化:
1.创建迁移:dotnet ef migrations add InitialCreate;
2.应用迁移:dotnet ef database update;
3.回滚迁移:dotnet ef database update
4. 常见问题与解决
4.1 实体无主键
错误:无法为实体类型'User'找到合适的主键;
解决:添加Id字段或用[Key]标记主键。
4.2 N+1查询问题
症状:查询100个用户,生成101个SQL(1个查用户+100个查订单);
解决:用Include(u => u.Orders)立即加载导航属性。
4.3 循环引用序列化
症状:序列化User时,Orders→User→Orders无限循环;
解决:关闭延迟加载,或配置序列化工具忽略循环引用(如Newtonsoft.Json的ReferenceLoopHandling.Ignore)。
4.4 连接字符串硬编码
解决:生产环境用配置文件(如appsettings.json)存储连接字符串,通过IConfiguration读取。
5. 最佳实践
1.DbContext依赖注入:ASP.NET Core中用AddDbContext注册Scoped生命周期的DbContext;
2.优先Fluent API:复杂映射用Fluent API,代码更易维护;
3.批量操作优化:用AddRange/RemoveRange代替循环Add/Remove;
4.避免长期持有DbContext:每次操作创建新实例,防止内存泄漏;
5.** 用迁移管理schema**:禁止手动修改数据库,所有变化通过迁移实现。
总结
EF Core是.NET开发者必备的ORM工具,核心价值在于简化数据库操作与提高开发效率。本节覆盖了映射、查询、优化的基础用法,后续可深入学习性能调优、多租户、读写分离等高级特性。掌握EF Core,你将告别手写SQL的繁琐,专注于业务逻辑实现。
(本节完)
下一章:EF Core高级查询与性能优化
(深入讲解复杂查询、索引优化、批量操作)
本站原创,转载请注明出处:










