从后台任务访问DbContext服务

时间:2018-07-28 15:12:01

标签: asp.net-core dependency-injection asp.net-core-2.1

因此,ASP.NET Core应用程序内置了依赖关系注入。借助Entity Framework Core,您可以轻松地从控制器操作方法中获取具有范围的DbContext实例。

但这仅限于控制器动作。如果您需要从某个动作开始长时间运行的后台任务,该动作将通过WebSocket等其他方式与浏览器中的视图进行通信,那么您突然一无所有。后台任务无法使用操作的DbContext,因为该操作已在操作返回时进行了范围划分和处置。

一种简单的方法是使用人们称为服务定位器的服务。这是某些IServiceProvider的静态副本,用于以后的访问和服务解析。 (我读过in this comment时,ASP.NET Core 2.1可能需要不同的方法。)但是无论我看什么,这都被称为反模式。它使测试更加困难并且混淆了依赖性。好吧。

那么对于这种情况推荐的解决方案是什么?我在茫茫荒野中的某个地方。甚至可以从调度程序而不是控制器操作启动的后台任务。附近没有HTTP请求。 DI在这里可以为我做什么?有没有不退后于反模式的解决方案?我确定ASP.NET Core DI的创建者已经想到了这一点。

是否有办法使服务在那里解决,或者更改我的体系结构以使后台任务本身以某种方式脱离DI?

更新:注释请求,例如:控制器动作启动某件事。就像网络扫描一样,这将需要很长时间。该视图返回类似“尊敬的用户,请稍等,您可以观看此进度条”。工作在后台继续,不断将进度和/或结果发布到浏览器。 (浏览器也可能会轮询进度。)后台任务需要访问数据库以存储扫描结果。扫描完成后,浏览器可以通过另一个操作来获取它。因此,如果后台任务仅使用控制器动作的DbContext,则在动作完成后将变得不可用。

另一个示例是与请求完全无关的后台服务。定期检查数据库然后执行某些操作的服务。这还需要一个DbContext,甚至没有地方可以尝试窃取它。

1 个答案:

答案 0 :(得分:5)

在这种情况下,您只能依靠作为反模式的servicelocater模式。另外,DbContext必须是每个实例独立的(临时的)实例,而不是作用域的对象。

我的建议是注入IServiceScopeFactory,这是一个单例,并开始在后台工作人员的工作范围中执行任务。

using (var scope = _serviceScopeFactory.CreateScope())
{
   var context = scope.ServiceProvider.GetRequiredService<DbContext>();
   // now do your work
}

没有别的了。这些东西不是mvc管道的一部分,因此无法即时解决。我也建议不要在某个地方直接访问dbcontext。将内容包装在存储库中,然后使用该存储库,甚至将该存储库包装在所谓的服务类中。这样,您的逻辑就被封装了,您的后台工作人员在收到消息时就执行一些事情。

没有直接关系,但是ASP.NET-Core具有IHostedService内置的背景工作人员。

编辑:如下面的Steven所建议的那样,在手动解析/启动类时,它可能并不总是反模式。至少当compositionroot照顾它时,不是这样。这是他提供的link,解释得很好。