如何将DbContext实例注入IHostedService?


80

如何将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班上。

但是,即使根据解决的Github问题DbContextOptions传递的应该具有与AddDbContext调用中指定的生存期相同的错误,但错误仍然相同。

我不能使数据库上下文成为单例,否则并发调用将产生并发异常(由于数据库上下文不能保证是线程安全的)。


也许注入上下文工厂呢?例如stackoverflow.com/a/11748370/1663001
DavidG,

Answers:


149

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

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

3
谢谢。注入IServiceScopeFactoryIServiceProvider直接注入之间有什么区别?

4
IServiceProvider只会给您根服务提供商。尽管它还实现了创建范围的接口,但是您可以使用它。但一般规则是要求的数量尽可能少。
马丁·乌尔里希

如果我DoWork在该using子句中产生一个新线程(该线程的寿命将超过),并使用我在其中获取的某些服务,可以吗?还是范围定义了服务何时处置?

范围将定义处理对象的时间。请注意,使用池化时,dbcontexts可能会被另一个组件重用。我只有使用async / await ..的托管服务代码..像await Task.Delay(…)做长时间运行的预定工作..这样,我可以轻松地在其上使用。
Martin Ullrich '18

8
AddDbContext()EF提供的默认方法仅将其注册为作用域。在作用域末尾,EF将进行清理。您不想在Web应用程序中使用单例数据库上下文,否则所有组件都会与其他组件的事务混为一谈。所有使用db上下文实例(通过构造函数注入)的服务也需要确定范围。
马丁·乌尔里希

0

您可以在构造函数中添加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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.