我应该如何将 DbContext 实例注入 IHostedService?

Posted

技术标签:

【中文标题】我应该如何将 DbContext 实例注入 IHostedService?【英文标题】:How should I inject a DbContext instance into an IHostedService? 【发布时间】:2018-06-30 07:40:41 【问题描述】:

问题

我应该如何将DbContext 实例注入(使用标准依赖注入)到IHostedService 中?

我尝试了什么

目前我的IHostedService 类在构造函数中采用MainContext(派生自DbContext)实例。

当我运行应用程序时,我得到:

无法使用单例“Microsoft.Extensions.Hosting.IHostedService”中的范围服务“Microsoft.EntityFrameworkCore.DbContextOptions”。

所以我尝试通过指定使DbContextOptions 瞬态:

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

在我的Startup 班级中。

但错误仍然存​​在,即使根据this solved Github issue 传递的DbContextOptions 应该具有在AddDbContext 调用中指定的相同生命周期。

我不能将数据库上下文设为单例,否则对它的并发调用会产生并发异常(因为不能保证数据库上下文是线程安全的)。

【问题讨论】:

也许注入一个上下文工厂?例如***.com/a/11748370/1663001 【参考方案1】:

在托管服务中使用服务的一个好方法是在需要时创建一个范围。这允许将服务/数据库上下文等与它们设置的生命周期配置一起使用。理论上,不创建范围可能会导致创建单例 DbContext 和不正确的上下文重用(EF Core 2.0 与 DbContext 池)。

为此,请注入 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>();
            …
        
    
    …

【讨论】:

谢谢。注入IServiceScopeFactory和直接注入IServiceProvider有什么区别? IServiceProvider 只会给你根服务提供者。虽然它还实现了创建范围的接口,但您可以使用它。但一般规则是要求尽可能少。 EF 提供的默认AddDbContext() 方法仅将其注册为作用域。例如,在范围结束时,EF 将进行清理。您不希望在 Web 应用程序中使用单例数据库上下文,否则您的所有组件都会与其他组件的事务混淆。所有使用 db 上下文实例(通过构造函数注入)的服务也需要限定范围。 嗨@MartinUllrich,这真的很有用,谢谢!我在我的 BackgroundService 中使用了建议的模式,一切运行正常。但我仍然感到困惑:我的 DBContext 被包装在一个存储库中,我将存储库和 DBContext 作为 Program.cs 的 CreateHostBuilder 中的单例添加到服务集合中......是我的 BackgroundService 得到它的“自己的”单例存储库/DBContext通过注入的 IServiceScopeFactory? @Peter 应该给你同样的例子。但是,我建议您在此处使用作用域生命周期(如果不可能,则使用瞬态),因为 EF Core / DbContext 不是线程安全的,并且从多个后台服务或控制器中使用它可能会导致问题【参考方案2】:

您可以在构造函数中添加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;

【讨论】:

以上是关于我应该如何将 DbContext 实例注入 IHostedService?的主要内容,如果未能解决你的问题,请参考以下文章

将 DbContext 与依赖注入一起使用

如何使用autofac实现多个DbContext的注入

如何使用 caliburn micro 在 Wpf 中注入 EF DbContext

如何将 DBContext 注入 HTTPModule

如何刷新实体框架核心 DBContext?

ASP.NET Core DbContext 注入