从后台任务访问DbContext服务

ygo*_*goe 7 dependency-injection asp.net-core asp.net-core-2.1

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

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

一种简单的方法是使用人们所谓的服务定位器。这是某些IServiceProvider的静态副本,用于以后的访问和服务解析。(ASP.NET Core 2.1可能需要使用与我在此注释中阅读的方法不同的方法。)但是,无论我看什么,都将其描述为反模式。它使测试更加困难并且混淆了依赖性。好的。

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

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

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

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

als*_*ami 7

在这种情况下,您只能依靠作为反模式的servicelocater模式。同样,DbContext必须是每个实例独立的(瞬态),而不是作用域。

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

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

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

没有直接关系,但是ASP.NET-Core具有IHostedService内置的后台工作

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

  • 我讨厌它是迂腐的,但是对`ServiceProvider.GetService` 的调用是否是服务定位器反模式的实现,取决于使用它的*位置*。如果从 [Composition Root](https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/) 内部调用,它*不是*服务定位器。但是当在组合根的*外部*使用时,它是一个服务定位器。有关详细信息,请参阅 [此](https://blog.ploeh.dk/2011/08/25/ServiceLocatorrolesvs.mechanics/)。 (3认同)