从后台任务访问DbContext服务 [英] Access DbContext service from background task

查看:336
本文介绍了从后台任务访问DbContext服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So ASP.NET Core applications have Dependency Injection built right in. And with Entity Framework Core, you can easily get a scoped DbContext instance from a controller action method.

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

But this is all limited to controller actions. If you need to start a long-running background task from an action that will communicate with the view in the browser by other means like WebSocket, then you suddenly have nothing at all. The background task can't use the action's DbContext because that was scoped and disposed of when the action returns.

一种简单的方法是使用人们称为服务定位器的服务.这是某些IServiceProvider的静态副本,用于以后的访问和服务解析. (ASP.NET Core 2.1可能需要其他方法,因为我已阅读

An easy way would be to use what people call a Service Locator. This is a static copy of some IServiceProvider for later access and service resolution. (ASP.NET Core 2.1 might need a different approach as I've read in this comment.) But whereever I look, this is described as anti-pattern. It makes testing harder and obfuscates dependencies. Alright.

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

So what's the recommended solution for this scenario? I'm somewhere in the middle of nowhere. A background task that might even be started from a scheduler instead of a controller action. No HTTP request anywhere near. What can DI do for me here? Is there a solution without falling back to anti-patterns? I'm sure the creators of ASP.NET Core DI have thought of this.

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

Is there either a way to get the services resolved there, or change my architecture so that the background task itself comes out of DI somehow?

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

Update: Requested by a comment, an example: A controller action starts something. This will take a long time, like a network scan. The view returns with something like "Dear user, please wait while you can watch this progress bar." Work continues in the background, continually posting the progress and/or results to the browser. (The browser might also poll progress instead.) The background task needs access to the database to store the scan results. When the scan is finished, the browser can fetch it through another action. So if the background task would just use the controller action's DbContext, that would become unusable as the action has completed.

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

Another example is a background service that isn't related to a request at all. A service that regularly checks the database and then does something. That also needs a DbContext and has nowhere to even try to steal it.

推荐答案

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

In that case you can only rely on the servicelocater-pattern which is an anti-pattern. Also the DbContext must be instance-per-dependency (transient) rather than scoped.

我的建议是注入IServiceScopeFactory这是一个单例,并在您的后台工作人员中开始做事的范围.

My suggestion is to inject IServiceScopeFactory which is a singleton and the beginn a scope in your background-worker that does the deed.

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

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

There is no other. These things are not part of the mvc pipeline so they cannot be resolved on the fly. I would also suggest not to directly access the dbcontext somewhere. Wrap things in a repository and then use that repository or even wrap that repository in a so called service class. So your logic is encapsulated and your background-worker just executes things when receiving a message.

没有直接关系,但是ASP.NET-Core具有内置的背景工作器,其中包含

Not directly related, but ASP.NET-Core has background-worker built-in with IHostedService.

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

As Steven suggested below, it might not always be an anti-pattern when manually resolving / initiating classes. At least not when the compositionroot is taking care of it. Here is a link he provided, where it is explained quite good.

这篇关于从后台任务访问DbContext服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆