什么是 `DbContext`

1 min read

DbContext 可以理解为 你和数据库的桥梁,每次你要操作数据库时,都会开启一个 "会话"(session),这个会话就是 DbContext 的实例。你可以用它来查询数据、添加数据、更新数据、删除数据。

DbContext 用了两种设计模式

  1. Unit of Work(工作单元模式) 👉 把多个数据库操作打包成一个事务,确保它们要么全部成功,要么全部失败,避免数据不一致。
  2. Repository(仓储模式) 👉 让你可以用对象(C# 类)来操作数据库,而不需要直接写 SQL。

为什么 DbContext 不能被多个线程同时使用?

简单来说:一个 DbContext 实例不能同时被多个地方用,尤其是并发执行的异步任务!

  • DbContext 内部有状态,如果多个线程同时修改它,就可能导致数据混乱,甚至程序崩溃。
  • 异步查询必须用 await,这样能确保查询完成后再执行下一步,不会产生并发问题。
  • 如果需要并发处理,应该创建多个 DbContext 实例,而不是让多个线程共用同一个实例。

正确示例(使用 await,保证异步执行顺序):

1 var users = await context.Users.ToListAsync(); // 先执行查询,等它完成后再执行下一步

错误示例(多个线程同时访问同一个 DbContext,可能会崩溃):

1 2 3 var task1 = context.Users.ToListAsync(); // 开始查询 var task2 = context.Orders.ToListAsync(); // 同时开始查询订单 await Task.WhenAll(task1, task2); // 并发执行(可能会出问题)

⚠ 解决方案:给 task2 用新的 DbContext 实例!


如何定义 DbContext

通常,我们需要创建一个 继承自 DbContext 的类,并在里面定义 DbSet(表示数据库表)。

示例:

1 2 3 4 5 public class AppDbContext : DbContext { public DbSet<User> Users { get; set; } // Users 表 public DbSet<Order> Orders { get; set; } // Orders 表 }

注意:如果 DbSetpublicsetDbContext 创建时会自动初始化这些 DbSet,你就可以直接使用它们查询数据库!


总结

  • DbContext 是你和数据库的桥梁,用来查询、保存数据。
  • 它结合了 "工作单元" 和 "仓储模式",简化数据库操作。
  • 不能在多个线程中同时使用同一个 DbContext 实例,否则会出错!
  • 异步查询必须 await,并发操作要用不同的 DbContext 实例
  • DbContext 里通常有 DbSet,每个 DbSet 代表数据库中的一张表。