雷达智富

首页 > 内容 > 程序笔记 > 正文

程序笔记

.NET InvalidOperationException: Cannot consume scoped service from singleton

2024-06-19 83

在.NET windows服务里注入DBContext时会报错:.NET InvalidOperationException: Cannot consume scoped service from singleton。

报错的代码如下:

builder.Services.AddDbContext<ToolsContext>(options => {
    options.UseSqlServer(builder.Configuration.GetConnectionString("Tools"));
});

System.AggregateException:“Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.EntityFrameworkCore.IDbContextFactory`1[Models.Tools.ToolsContext] Lifetime: Singleton ImplementationType: Microsoft.EntityFrameworkCore.Internal.DbContextFactory`1[Models.Tools.ToolsContext]': Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions`1[Models.Tools.ToolsContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory`1[Models.Tools.ToolsContext]'.) (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: MyTools.Service.Worker': Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions`1[Models.Tools.ToolsContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory`1[Models.Tools.ToolsContext]'.)”

主要原因是,IHostedService实例中可以注入的对象有限制。我们可以注入的两种依赖注入生命周期是Singleton 和 _Transient_。这使得我们不能像ASP.NET Core那样直接将一个DbContext注入,因为AddDbContext<TContext>()方法会将我们的上下文注册为Scoped服务。

解决方法是使用IDbContextFactory将DbContext实例注入到IHostedService,这将把一个IDbContextFactory<TContext>实例作为一个Singleton服务添加到DI容器中,代码如下:

builder.Services.AddDbContextFactory<ToolsContext>(options => {
    options.UseSqlServer(builder.Configuration.GetConnectionString("Tools"));
});

在需要使用DBContext的地方,我们在注入的IDbContextFactory<ToolsContext>上调用CreateDbContextAsync()方法。这将初始化我们的DbContext类,然后我们可以用它来操作数据库了。代码如下:

IDbContextFactory<ToolsContext> _contextFactory;

public Worker(IDbContextFactory<ToolsContext> contextFactory)
{
    _contextFactory = contextFactory;
}

// 获取DBContext对象
using (var _toolContext = await _contextFactory.CreateDbContextAsync(stoppingToken)) {
    var webSites = await _toolContext.WebSites.ToListAsync();
}

这样就不会报错了。

更新于:5个月前
赞一波!2

文章评论

评论问答