从后台任务访问 DbContext 服务

Access DbContext service from background task

因此 ASP.NET 核心应用程序内置了依赖注入。使用 Entity Framework 核心,您可以轻松地从控制器操作方法中获取作用域 DbContext 实例。

但这都仅限于控制器操作。如果您需要从一个将通过 WebSocket 等其他方式与浏览器中的视图通信的操作启动一个长 运行 的后台任务,那么您突然什么都没有了。后台任务无法使用操作的 DbContext,因为它在操作 returns.

时被限定范围并被释放

一种简单的方法是使用人们所说的服务定位器。这是一些 IServiceProvider 的静态副本,用于以后访问和服务解析。 (ASP.NET Core 2.1 可能需要一种不同的方法,因为我已经阅读过 。)但是无论我在哪里看,这都被描述为反模式。它使测试更加困难并混淆了依赖关系。好的。

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

有没有办法在那里解决服务,或者改变我的架构,以便后台任务本身以某种方式从 DI 中出来?

更新:评论请求,一个例子:一个控制器动作启动了一些东西。这将需要很长时间,就像网络扫描一样。带有类似 "Dear user, please wait while you can watch this progress bar." 的视图 returns 工作在后台继续,不断将进度 and/or 结果发布到浏览器。 (浏览器也可能会轮询进度。)后台任务需要访问数据库以存储扫描结果。扫描完成后,浏览器可以通过另一个操作获取它。因此,如果后台任务只使用控制器操作的 DbContext,那么当操作完成时,它将变得不可用。

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

在这种情况下,您只能依赖反模式的 servicelocater-pattern。此外,DbContext 必须是每个依赖实例(瞬态)而不是作用域。

我的建议是注入 IServiceScopeFactory 这是一个单例,并在你的后台工作人员中开始一个范围来完成这项工作。

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

没有别的了。这些东西不是 mvc 管道的一部分,因此无法即时解决。我还建议不要在某处直接访问 dbcontext。将东西包装在存储库中,然后使用该存储库,甚至将该存储库包装在所谓的服务中 class。所以你的逻辑被封装了,你的后台工作者只是在收到消息时执行事情。

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

编辑: 正如史蒂文在下面建议的那样,手动解析/启动 classes 时它可能并不总是反模式。至少在 compositionroot 处理它时不会。这是他提供的link,里面解释的很好。

我在将我的 .Net 4.8 项目更新到 Net 6 的同时考虑将定时进程移动到后台服务时发现了这个问答。看来您不再需要像 alsami 的回答那样将 dbcontext 配置为瞬态状态。至少,我不再收到错误:singleton cannot consume scoped.

Implicitly sharing DbContext instances via dependency injection

Using dependency injection, this can be achieved by either registering the context as scoped, and creating scopes (using IServiceScopeFactory) for each thread, or by registering the DbContext as transient (using the overload of AddDbContext which takes a ServiceLifetime parameter).