问题
我应该如何将 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
调用中指定的相同生命周期。
我不能使数据库上下文成为单例,否则对它的并发调用会产生并发异常(由于不能保证数据库上下文是线程安全的)。
在托管服务中使用服务的一个好方法是在需要时创建一个范围。这允许将服务/数据库上下文等与它们设置的生命周期配置一起使用。理论上,不创建范围可能会导致创建单例 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>();
…
}
}
…
}
您可以在构造函数中添加创建范围,如下所示:
public ServiceBusQueueListner(ILogger<ServiceBusQueueListner> logger, IServiceProvider serviceProvider, IConfiguration configuration)
{
_logger = logger;
_reportProcessor = serviceProvider.CreateScope().ServiceProvider.GetRequiredService<IReportProcessor>();
_configuration = configuration;
}
做添加
using Microsoft.Extensions.DependencyInjection;
不定期副业成功案例分享
IServiceScopeFactory
和直接注入IServiceProvider
有什么区别?IServiceProvider
将只为您提供根服务提供商。虽然它还实现了创建范围的接口,但您可以使用它。但一般规则是要求尽可能少。AddDbContext()
方法仅将其注册为作用域。例如,在范围结束时,EF 将进行清理。您不希望在 Web 应用程序中使用单例数据库上下文,否则您的所有组件都会与其他组件的事务混淆。所有使用 db 上下文实例(通过构造函数注入)的服务也需要限定范围。