.NET 8中服务调用DbContext报错,如何在控制器外使用上下文?
在.NET 8中控制器外使用DbContext的问题排查与解决
问题描述
在.NET 8环境下,API控制器中使用SomeContext可正常工作,但在CqcService中注入并使用该上下文时,抛出数据库连接错误:
Microsoft.EntityFrameworkCore.Database.Connection[20004]
使用服务器'(localdb)\mssqllocaldb'上的数据库'SomeContext'的连接时发生错误。
相关配置与代码:
- 连接字符串:
"ConnectionStrings": { "SomeContext": "Server=(localdb)\\mssqllocaldb;Database=SomeContext;Trusted_Connection=True;MultipleActiveResultSets=true" }
- Program.cs中的DbContext注册:
builder.Services.AddDbContext<SomeContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("SomeContext") ?? throw new InvalidOperationException("Connection string 'SomeContext' not found.")));
- 报错的服务代码:
private readonly SomeContext _context; public CqcService(SomeContext context) { _context = context; } public async Task<Provider?> GetProvider(string id) { var dbProvider = await _context.Provider.SingleOrDefaultAsync(x => x.ProviderId == id); return dbProvider; }
解决方案
1. 确保服务已注册到依赖注入容器
控制器能正常注入DbContext说明上下文注册无问题,但CqcService本身需要被注册到DI容器才能实现构造函数注入。在Program.cs中添加服务注册:
// 根据业务需求选择Scoped/Transient/Singleton,注意与DbContext生命周期匹配 builder.Services.AddScoped<CqcService>();
2. 检查服务与DbContext的生命周期兼容性
默认AddDbContext注册的DbContext为Scoped生命周期(每个请求创建一个实例)。如果CqcService被注册为Singleton,注入Scoped的DbContext会导致上下文在请求结束后被释放,后续使用时触发连接错误。
解决方式:
- 将服务改为Scoped或Transient(推荐与DbContext生命周期一致):
builder.Services.AddScoped<CqcService>(); // 或 builder.Services.AddTransient<CqcService>();
- 若必须使用Singleton服务,改用
AddDbContextFactory创建上下文实例:
// 注册DbContext工厂 builder.Services.AddDbContextFactory<SomeContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("SomeContext")));
然后在服务中注入工厂,使用时创建上下文:
private readonly IDbContextFactory<SomeContext> _contextFactory; public CqcService(IDbContextFactory<SomeContext> contextFactory) { _contextFactory = contextFactory; } public async Task<Provider?> GetProvider(string id) { using var context = _contextFactory.CreateDbContext(); var dbProvider = await context.Provider.SingleOrDefaultAsync(x => x.ProviderId == id); return dbProvider; }
3. 避免同步调用异步方法导致死锁
如果服务方法被同步代码调用(如使用.Result或.Wait()),可能引发线程死锁,进而导致数据库连接异常。确保全程使用异步调用:
// 错误示例:同步调用异步方法 var provider = cqcService.GetProvider(id).Result; // 正确示例:使用await var provider = await cqcService.GetProvider(id);
4. 验证DbContext的定义
检查SomeContext类是否正确继承DbContext,且未在OnConfiguring方法中硬编码连接字符串覆盖配置:
public class SomeContext : DbContext { public SomeContext(DbContextOptions<SomeContext> options) : base(options) { } // 不要在OnConfiguring中硬编码连接字符串,除非是开发环境临时测试 // protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) // { // } public DbSet<Provider> Provider { get; set; } }
5. 检查LocalDB运行状态
若以上配置均无问题,可排查LocalDB本身:
- 打开命令提示符,执行
sqllocaldb info查看实例状态 - 若实例未运行,执行
sqllocaldb start mssqllocaldb启动 - 必要时删除并重建实例:
sqllocaldb delete mssqllocaldb→sqllocaldb create mssqllocaldb
内容的提问来源于stack exchange,提问作者SmallWorld
相关产品推荐
相关产品推荐

