以下是基于 Entity Framework Core 的 CRUD 操作与关联查询实战示例,以 用户(User) 和 订单(Order) 实体为例(一对多关系),包含完整代码和操作说明。
一、基础准备
1. 实体类定义(沿用之前的数据注解配置)
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; // 用户实体 public class User {[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]public int UserId { get; set; } [Required][StringLength(50)]public string UserName { get; set; } [Required][DataType(DataType.EmailAddress)]public string Email { get; set; } [Timestamp]public byte[] RowVersion { get; set; } // 导航属性:一个用户包含多个订单public ICollection<Order> Orders { get; set; } = new List<Order>(); } // 订单实体 public class Order {[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]public int OrderId { get; set; } [Required][Column(TypeName = "decimal(18,2)")]public decimal Amount { get; set; } public DateTime OrderTime { get; set; } = DateTime.Now; // 外键:关联用户[ForeignKey("User")]public int UserId { get; set; } // 导航属性:一个订单属于一个用户public User User { get; set; } }
2. DbContext 定义(数据库上下文)
using Microsoft.EntityFrameworkCore; public class AppDbContext : DbContext {// 数据集(对应数据库表)public DbSet<User> Users { get; set; }public DbSet<Order> Orders { get; set; } // 配置数据库连接(示例使用 SQL Server LocalDB)protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EfCrudDemo;Trusted_Connection=True;");} // 可选:用 Fluent API 补充配置(与数据注解不冲突,优先级更高)protected override void OnModelCreating(ModelBuilder modelBuilder){// 配置 User 与 Order 的一对多关系modelBuilder.Entity<Order>().HasOne(o => o.User).WithMany(u => u.Orders).HasForeignKey(o => o.UserId).OnDelete(DeleteBehavior.Cascade); // 级联删除:删除用户时删除其所有订单} }
二、CRUD 操作实战
以下示例封装了用户和订单的 CRUD 操作,使用异步方法(EF 推荐)并处理常见异常。
1. 创建操作(Create)
using System.Threading.Tasks; public class CrudService {private readonly AppDbContext _context; // 依赖注入 DbContextpublic CrudService(AppDbContext context){_context = context;} // 创建用户public async Task<User> CreateUserAsync(string userName, string email){var user = new User{UserName = userName,Email = email}; _context.Users.Add(user);await _context.SaveChangesAsync(); // 提交到数据库return user;} // 为用户创建订单public async Task<Order> CreateOrderAsync(int userId, decimal amount){// 先验证用户是否存在var user = await _context.Users.FindAsync(userId);if (user == null)throw new KeyNotFoundException("用户不存在"); var order = new Order{UserId = userId,Amount = amount,OrderTime = DateTime.Now}; _context.Orders.Add(order);await _context.SaveChangesAsync();return order;} }
2. 读取操作(Read)
包括单条查询、列表查询、条件查询,以及关联查询(核心)。
using Microsoft.EntityFrameworkCore; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; public class QueryService {private readonly AppDbContext _context; public QueryService(AppDbContext context){_context = context;} // 1. 查询单个用户(不含关联订单)public async Task<User> GetUserByIdAsync(int userId){return await _context.Users.FindAsync(userId);} // 2. 关联查询:查询用户及其所有订单(一对多关联)public async Task<User> GetUserWithOrdersAsync(int userId){// 使用 Include 加载关联的 Orders 集合return await _context.Users.Include(u => u.Orders) // 关键:加载导航属性.FirstOrDefaultAsync(u => u.UserId == userId);} // 3. 关联查询:查询订单及其所属用户public async Task<Order> GetOrderWithUserAsync(int orderId){return await _context.Orders.Include(o => o.User) // 加载订单关联的 User.FirstOrDefaultAsync(o => o.OrderId == orderId);} // 4. 条件查询:查询金额大于 100 的订单及用户public async Task<List<Order>> GetHighValueOrdersAsync(decimal minAmount){return await _context.Orders.Include(o => o.User).Where(o => o.Amount > minAmount).OrderByDescending(o => o.OrderTime).ToListAsync();} // 5. 分页查询:查询用户列表(每页 10 条)public async Task<List<User>> GetUsersByPageAsync(int pageIndex, int pageSize){return await _context.Users.Skip((pageIndex - 1) * pageSize) // 跳过前面的页.Take(pageSize) // 取当前页数据.ToListAsync();} }
3. 更新操作(Update)
public async Task<bool> UpdateUserAsync(int userId, string newUserName, string newEmail) {// 方式1:先查询再更新(推荐,可避免并发问题)var user = await _context.Users.FindAsync(userId);if (user == null)return false; user.UserName = newUserName;user.Email = newEmail; // EF 会自动跟踪实体变化,无需显式调用 Updateawait _context.SaveChangesAsync();return true; // 方式2:直接附加实体更新(适合已知实体完整信息的场景)// var user = new User { UserId = userId, UserName = newUserName, Email = newEmail };// _context.Users.Update(user); // 显式标记为修改// await _context.SaveChangesAsync(); } // 更新订单金额(含并发控制) public async Task<bool> UpdateOrderAmountAsync(int orderId, decimal newAmount, byte[] rowVersion) {var order = await _context.Orders.FindAsync(orderId);if (order == null)return false; // 并发控制:检查时间戳是否匹配(防止多人同时修改)_context.Entry(order).Property("RowVersion").OriginalValue = rowVersion; order.Amount = newAmount;try{await _context.SaveChangesAsync();return true;}catch (DbUpdateConcurrencyException){// 并发冲突处理(如提示用户数据已被修改)return false;} }
4. 删除操作(Delete)
// 删除用户(级联删除其所有订单,在 OnModelCreating 中配置) public async Task<bool> DeleteUserAsync(int userId) {var user = await _context.Users.FindAsync(userId);if (user == null)return false; _context.Users.Remove(user);await _context.SaveChangesAsync();return true; } // 删除单个订单 public async Task<bool> DeleteOrderAsync(int orderId) {// 方式1:先查询再删除// var order = await _context.Orders.FindAsync(orderId);// if (order != null) _context.Orders.Remove(order); // 方式2:直接构造实体删除(性能更优,无需查询)var order = new Order { OrderId = orderId };_context.Orders.Remove(order); await _context.SaveChangesAsync();return true; }
三、关联查询高级场景
1. 过滤关联数据(只加载符合条件的订单)
// 查询用户及其金额大于 500 的订单 public async Task<User> GetUserWithHighValueOrdersAsync(int userId) {return await _context.Users.Include(u => u.Orders.Where(o => o.Amount > 500)) // 过滤关联集合.FirstOrDefaultAsync(u => u.UserId == userId); }
2. 多级关联查询(假设有三级实体:Order → OrderItem)
// 实体类补充:OrderItem(订单明细) public class OrderItem {public int Id { get; set; }public string ProductName { get; set; }public decimal Price { get; set; }public int OrderId { get; set; }public Order Order { get; set; } } // 查询订单及其用户、订单明细(三级关联) public async Task<Order> GetOrderWithDetailsAsync(int orderId) {return await _context.Orders.Include(o => o.User) // 一级关联:用户.Include(o => o.OrderItems) // 二级关联:订单明细.FirstOrDefaultAsync(o => o.OrderId == orderId); }
四、使用示例(控制台 / API)
// 初始化服务(实际项目中推荐依赖注入) using var context = new AppDbContext(); var crudService = new CrudService(context); var queryService = new QueryService(context); // 1. 创建用户和订单 var user = await crudService.CreateUserAsync("张三", "zhangsan@example.com"); var order1 = await crudService.CreateOrderAsync(user.UserId, 299.99m); var order2 = await crudService.CreateOrderAsync(user.UserId, 899.99m); // 2. 关联查询:查询用户及其所有订单 var userWithOrders = await queryService.GetUserWithOrdersAsync(user.UserId); Console.WriteLine($"用户 {userWithOrders.UserName} 的订单数:{userWithOrders.Orders.Count}"); // 3. 更新用户信息 await crudService.UpdateUserAsync(user.UserId, "张三三", "zhangsan3@example.com"); // 4. 删除订单 await crudService.DeleteOrderAsync(order1.OrderId);
五、关键注意事项
关联数据加载:
显式加载:使用
Include
(立即加载)或ThenInclude
(多级加载)。延迟加载:在导航属性加
virtual
并配置UseLazyLoadingProxies()
,但可能导致 N+1 查询问题,谨慎使用。
并发控制: 使用
[Timestamp]
标记的字段可自动处理并发,SaveChanges
时若发现数据已被修改会抛出DbUpdateConcurrencyException
。性能优化:
避免加载不必要的字段:使用
Select
投影(_context.Users.Select(u => new { u.UserId, u.UserName })
)。分页查询:
Skip()
+Take()
减少数据传输量。批量操作:复杂批量更新 / 删除推荐用
EF Core Bulk Extensions
库提升性能。
通过以上示例,可掌握 EF Core 中 CRUD 操作和关联查询的核心用法,覆盖大部分日常开发场景。