在Entity Framework Core里,DbContext是操作数据库的入口,负责实体跟踪、变更探测以及最终的SQL生成。创建DbContext并不是简单new一下就能用好,不同的应用形态对应不同的构建策略。理解这些方式背后的生命周期管理,才能避免连接泄漏与状态混乱。

一、通过重写OnConfiguring在内部配置
最直观的创建方式是定义一个继承自DbContext的类,并在其中重写OnConfiguring方法。该方法会在上下文第一次被实例化且尚未配置时调用,我们可以在这里用UseSqlServer等扩展方法把连接字符串写死或使用配置变量。
这种写法适合小型控制台工具或快速原型,因为不需要引入依赖注入容器。但缺点也很明显:连接字符串耦合在代码里,不利于多环境切换;而且每次new MyContext()都会走一遍配置逻辑。下面给出一个最小示例:
using Microsoft.EntityFrameworkCore;
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 直接指定SQL Server连接串
optionsBuilder.UseSqlServer("Server=.;Database=BlogDb;Trusted_Connection=True;");
}
}
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
}
使用时代码非常直白,但请留意:若你在循环里频繁new BlogContext(),每个实例都持有独立连接与跟踪器,不仅浪费资源,还可能在未释放时耗尽连接池。因此这种形式只建议用在短命脚本中。
二、使用DbContextOptions显式传入构造
更灵活的做法是把配置外置,通过DbContextOptions对象传给自定义构造函数。这样上下文类本身不关心连哪台库,调用方决定。该方式方便单元测试时换内存库,也利于在大型项目里集中管理配置。
我们可以让上下文接收一个DbContextOptions<T>参数并向上传递给基类。创建实例前,先用DbContextOptionsBuilder构建选项。以下示例展示如何在控制台主程序里手动构造:
using Microsoft.EntityFrameworkCore;
public class AppContext : DbContext
{
public AppContext(DbContextOptions<AppContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
// 调用端
var builder = new DbContextOptionsBuilder<AppContext>();
builder.UseSqlServer("Server=.;Database=AppDb;Trusted_Connection=True;");
using var ctx = new AppContext(builder.Options);
这种模式的优势在于上下文保持纯净,所有环境差异由外部选项控制。如果需要在同一进程里访问多个数据库,只要准备不同的options即可。不过在Web场景下手写依然繁琐,于是有了依赖注入方案。
三、ASP.NET Core中的依赖注入注册
在Web应用或后台服务里,推荐用服务容器管理DbContext。框架提供的AddDbContext扩展会把上下文以 scoped 生命周期注册:每个HTTP请求拿到同一个实例,请求结束自动释放。这既保证了实体跟踪的连续性,又避免手动释放疏忽。
在Program.cs中,只需一行注册,之后在控制器或服务构造函数声明即可注入。下面的代码演示最小API里的用法:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppContext>(opt =>
opt.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
var app = builder.Build();
app.MapGet("/users", async (AppContext db) =>
{
return await db.Users.ToListAsync();
});
app.Run();
使用注入后,你不应再自己new上下文,否则会绕过容器生命周期。若某些场景要脱离请求临时建上下文,可注入IDbContextFactory<T>,用工厂CreateDbContext方法按需生成,特别适合后台任务或并行处理。下表归纳三种方式差异:
| 方式 | 适用场景 | 生命周期管理 |
|---|---|---|
| 重写OnConfiguring | 控制台脚本、demo | 调用方手动using |
| 传入DbContextOptions | 多环境、可测试代码 | 调用方控制 |
| AddDbContext注入 | Web、长驻服务 | 容器scoped/单例 |
总结来看,选择哪种创建方式取决于运行环境。中小工具可用前两种快速落地;正式服务务必交给依赖注入,既省心又稳当。理清DbContext的构造逻辑,是写好EF Core数据层的第一步。