Dependency injection 通过依赖项注入用于后台任务的DbContext

Dependency injection 通过依赖项注入用于后台任务的DbContext,dependency-injection,asp.net-core,asp.net-core-mvc,background-process,entity-framework-core,Dependency Injection,Asp.net Core,Asp.net Core Mvc,Background Process,Entity Framework Core,我可能没有朝着正确的方向思考。我对依赖注入和ASP.NETCore相当陌生 我有一个ASP.Net核心网站,其中一项任务是将数据从excel工作表导入用户将上载的数据库。excel表格可能非常庞大,数据转换任务非常耗时,因此我希望在后台执行它们。i、 e.用户将上传工作表,立即发送响应,后台作业/线程将导入数据 我正在尝试通过以下方式运行后台作业: Task.Run(() => ProcessImport(model)); 我遇到的问题是,Process import方法调用具有存储库类

我可能没有朝着正确的方向思考。我对依赖注入和ASP.NETCore相当陌生

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

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

Task.Run(() => ProcessImport(model));
我遇到的问题是,Process import方法调用具有存储库类的服务,这些存储库类通过ASP.Net依赖项注入容器访问AppDbContext,该容器作为作用域添加,并且在发送回响应后,该上下文将被释放。我得到一个运行时异常,在上下文被处理后,您不能使用它


我的问题是,处理这种情况的最佳方式是什么?我应该将AppDbContext设置为单例吗?我是否应该在ProcessImport方法中创建一个新的AppDbContext实例,并传递它?我已经读过DbContext不是线程安全的,所以这是一个好方法吗?

您应该将
IServiceScopeFactory
实例(它是单例)传递到任务中

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

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


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

要细分您的问题:

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

    API处理长时间运行的任务并不理想。您可以将该过程委托给后台应用程序


  • 我应该将AppDbContext设置为单例吗

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

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

    细分各种服务/流程:

    • 将获取文件的API。理想情况下,API应该将文件作为输入,并将其持久化到磁盘或数据库
    • 将处理该文件的服务。将此组件/服务创建为单独的库。然后从控制台应用程序使用此库。通过这种方式,您可以分析性能。通过使其成为异步方法,使其激发并忘记方法。这样,您就可以灵活地重用代码
    • 如果您不希望大量文件上载,那么可以在API控制器上重用库,并使用QueueBackgroundWorkItem执行该方法。请参见此处以供参考:
  • 我是否应该在ProcessImport方法中创建一个新的AppDbContext实例,并传递它

    因为它不是线程安全的,所以在每个线程中创建并使用dbContext类的单独实例

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

    有关使用EF dbContext的深入指南,请查看以下博客:


  • “我应该使AppDbContext成为单例吗?”不,你是对的,但是如何处理在不同线程上运行的任务,需要访问DbContext?无论如何,这是一种危险的模式。您不应该使用ASP.Net应用程序来创建。如果你做对了,任务就在自己的进程中运行,整个DI问题就不存在了。我不完全理解如何在我的场景中应用它。我有一个名为ImportService的类,它是从控制器调用的,我目前正在注入存储库类以访问数据库。我想在ProcessImport方法中使用这些存储库类。如果我理解正确,如果我使用(var scope=ServiceScopeFactory.CreateScope()){}将方法体括起来,它会使注入的类在块的生存期内保持不变,还是会注入这些类的新实例?否,新的作用域会给您db(和其他作用域生存期)类的新实例。如果不希望用户[request]等待ProcessImport完成,则不应与其共享作用域生存期实例。请求范围在请求结束时被释放,这将处理其中的所有一次性内容。我使用此建议构建了一个工厂,用于为多步骤工作流创建我的db上下文,该工作流一直在处理每个请求的服务提供商和db上下文。这是相同的想法,但您可能希望将作用域创建封装在另一个类中,正如我在本文的中间帖子中所解释的主题: