.net 如何从Singleton中使用Scoped服务?

ryevplcw  于 2023-05-30  发布在  .NET
关注(0)|答案(3)|浏览(471)

我应该如何注入(使用.NET Core的内置依赖注入库MS.DI)一个DbContext示例到Singleton中?在我的特定情况下,单例是IHostedService

我尝试了什么

我目前让我的IHostedService类在构造函数中接受一个MainContext(派生自DbContext)示例。
当我运行应用程序时,我得到:
无法从单例“Microsoft.Extensions.Hosting. IHostedService”使用作用域服务“Microsoft.EntityFrameworkCore.DbContextOptions”。
因此,我尝试通过指定使DbContextOptions成为 transient :

services.AddDbContext<MainContext>(options =>
    options.UseSqlite("Data Source=development.db"),
    ServiceLifetime.Transient);

在我的Startup类中。
但是错误仍然是相同的,即使根据this solved Github issue,传递的DbContextOptions应该具有与AddDbContext调用中指定的相同的生存期。
我不能使数据库上下文成为单例,否则对它的并发调用将产生并发异常(由于数据库上下文不能保证是线程安全的)。

qgzx9mmu

qgzx9mmu1#

在托管服务中使用服务的一个好方法是在需要时创建一个范围。这允许使用服务/数据库上下文等。使用它们设置的生存期配置。不创建作用域在理论上可能会导致创建单例DbContext和不正确的上下文重用(EF Core 2.0 with DbContext pools)。
为此,注入一个IServiceScopeFactory,并在需要时使用它创建一个作用域。然后从这个范围中解决您需要的任何依赖项。这还允许您将自定义服务注册为范围依赖项,如果您希望将逻辑移出托管服务并仅使用托管服务来触发某些工作(例如定期触发任务-这将定期创建范围,在该范围中创建任务服务,该任务服务还获得注入的数据库上下文)。

public class MyHostedService : IHostedService
{
    private readonly IServiceScopeFactory scopeFactory;

    public MyHostedService(IServiceScopeFactory scopeFactory)
    {
        this.scopeFactory = scopeFactory;
    }

    public void DoWork()
    {
        using (var scope = scopeFactory.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
            …
        }
    }
    …
}
72qzrwbm

72qzrwbm2#

可以在构造函数中添加create Scope,如下所示:

public ServiceBusQueueListner(ILogger<ServiceBusQueueListner> logger, IServiceProvider serviceProvider, IConfiguration configuration)
        {
            _logger = logger;
            _reportProcessor = serviceProvider.CreateScope().ServiceProvider.GetRequiredService<IReportProcessor>();
            _configuration = configuration;
        }

做加法

using Microsoft.Extensions.DependencyInjection;
dw1jzc5e

dw1jzc5e3#

针对单例服务消费DbContext的具体情况

自.NET 5起,您可以为DbContext注册IDbContextFactory<TContext>并将其注入到单例服务。

使用AddDbContextFactory<TContext>注册工厂:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContextFactory<MyDbContext>(
        options.UseSqlite("Data Source=development.db"));
}
  • 注意:从.NET 6开始,您可以在使用AddDbContextFactory()时删除AddDbContext(),因为后者也将上下文类型本身注册为作用域服务。

在singleton服务中,注入IDbContextFactory<TContext>并使用CreateDbContext()在需要的地方创建DbContext的示例:

public class MySingletonService : BackgroundService, IHostedService
{
    private readonly IDbContextFactory<MyDbContext> _contextFactory;

    public MySingletonService(IDbContextFactory<MyDbContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using (MyDbContext dbContext = _contextFactory.CreateDbContext())
        {
            await dbContext.MyData.ToListAsync(stoppingToken);
        }
    }
}

参见

MS Docs:使用DbContext工厂(例如Blazor)

备注

不要混淆:

相关问题