通过依赖注入实现后台任务的 DbContext

Posted

技术标签:

【中文标题】通过依赖注入实现后台任务的 DbContext【英文标题】:DbContext for background tasks via Dependency Injection 【发布时间】:2016-12-30 18:11:41 【问题描述】:

我可能没有朝着正确的方向思考。我对依赖注入和 ASP.Net Core 还很陌生。

我有一个 ASP.Net 核心网站,其中一项任务是将数据从 Excel 工作表导入用户将上传的数据库。 excel表格可能很大,数据转换任务很耗时,因此我希望在后台执行它们。即用户将上传工作表,响应将立即发送,后台作业/线程将导入数据。

我正在尝试通过以下方式运行后台作业:

Task.Run(() => ProcessImport(model));

我遇到的问题是 Process 导入方法调用具有存储库类的服务,这些服务通过添加为 Scoped 的 ASP.Net 依赖注入容器访问 AppDbContext ,并且一旦发送回响应,上下文就会被处理掉。我收到一个运行时异常,您在处理完上下文后无法使用它。

我的问题是,处理这种情况的最佳方法是什么?我应该使 AppDbContext 单例吗?我应该在 ProcessImport 方法中创建一个新的 AppDbContext 实例并将其传递吗?我读过 DbContext 不是线程安全的,这是一个好方法吗?

【问题讨论】:

"我应该将 AppDbContext 设为单例吗?"没有:***.com/questions/3266295/… 你是对的,但是如何处理在不同线程上运行的需要访问 DbContext 的任务? 无论如何这都是一种危险的模式。您不应该将 ASP.Net 应用程序用于execute long-running fire & forget tasks。如果你做对了,任务会在它自己的进程中运行,整个 DI 问题就消失了。 【参考方案1】:

您应该将 IServiceScopeFactory 实例(它是单例)传递给您的任务。

在任务内部,当数据到达时,您应该创建新的CreateScope() 并从该范围请求服务。数据处理完成后 - 处置此范围(但保留对 IServiceScopeFactory 的引用以供下次运行)。

例如见this。我用这个库运行小而快的任务。

正如 Gert 所写,对于繁重/长时间运行的任务,不要依赖于您的任务将始终运行到完成。准备好重启,准备好重新处理相同的数据。

【讨论】:

我不完全明白如何在我的场景中应用它。我有一个名为 ImportService 的类,它从控制器调用,我目前正在注入存储库类以访问数据库。我想在我的 ProcessImport 方法中使用这些存储库类。如果我理解正确,如果我用 using (var scope = ServiceScopeFactory.CreateScope()) 括住方法的主体,它会使注入的类在块的生命周期内持续存在,还是会注入这些的新实例上课? 不,新作用域将为您提供 db(和其他作用域生命周期)类的新实例。如果您不希望用户 [request] 等待您的 ProcessImport 完成 - 您不应与其共享范围生命周期实例。请求范围在请求结束时被释放,它会释放其中的所有一次性内容。 我使用这个建议构建了一个工厂,用于为多步骤工作流创建我的数据库上下文,该工作流只是让服务提供者和数据库上下文随每个请求一起处理。 同样的想法,但您可能希望将范围创建包装在另一个类中,如我在此主题的中型帖子中所述:medium.com/@michaelceber/…【参考方案2】:

分解您的问题:

    处理这种情况的最佳方法是什么?

    API 不适合处理长时间运行的任务。您可以将进程委托给后台应用程序

    我应该将 AppDbContext 设为单例吗?

    在 web 应用程序场景中,dbContext 不应该是单例的,因为它可能会带来管理事务等问题。

    处理这种情况的最佳方法是什么?

    分解各种服务/流程:

    将获取文件的 API。理想情况下,API 应该只将文件作为输入并将其保存到磁盘或数据库中。 将处理文件的服务。将此组件/服务创建为单独的库。然后从控制台应用程序使用这个库。这样您就可以分析性能。通过使其成为异步方法来使其触发并忘记方法。这样您就可以灵活地重用代码。 如果您不希望上传大量文件,您可以在 API 控制器上重用该库并使用 QueueBackgroundWorkItem 执行该方法。参考这里:http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx

    我是否应该在 ProcessImport 方法中创建一个新的 AppDbContext 实例并传递它?

    由于它不是线程安全的,因此请在每个线程中创建并使用 dbContext 类的单独实例。

    我读过 DbContext 不是线程安全的,那么这是一个好方法吗?

    有关使用 EF dbContext 的深入指南,请查看此博客:http://mehdi.me/ambient-dbcontext-in-ef6/

【讨论】:

以上是关于通过依赖注入实现后台任务的 DbContext的主要内容,如果未能解决你的问题,请参考以下文章

将 DbContext 与依赖注入一起使用

用Decorator实现依赖注入,像Java一样写后台

带有私有 NuGet 包的 Blazor 依赖项注入和 EF/DbContext

在依赖注入中使用 Entity Framework Core DbContext 时不调用 OnModelCreating

.NET Entity Framework Core、依赖注入和线程的 DBContext System.ObjectDisposed 异常

AutoFac实现WebAPI依赖注入(EF以及Mysql)