【问题标题】:Service lifetime in ASP.NET CoreASP.NET Core 中的服务生命周期
【发布时间】:2020-02-02 15:21:54
【问题描述】:

我有一个简单的问题,但在任何地方都没有找到答案。

解决方案包含两个 Web API。一个是 .NET Core 2.1 和 EF Core 2.1.1,第二个是 3.1 和 EF Core 3.1.1,我的代码是相同的。有一个自定义存储库和一个控制器。

个人资料库:

public PersonRepository(AppContext appContext)
{
   this.appContext = appContext;
}

public async Task<IEnumerable<Person>> GetAll()
{
    return await appContext.People.ToListAsync();
}

控制器:

public MyController(PersonRepository personRepository)
{
    this.personRepository = personRepository;
}

[HttpGet]
public async Task<ActionResult> Get()
{
    var data = personRepository.GetAll();
    var data1 = personRepository.GetAll();

    var result = await Task.WhenAll(data, data1);

    return Ok(data.Result);
}
services.AddDbContext<AppContext>(options => options
.UseSqlServer("")
.EnableSensitiveDataLogging(true));

这似乎是无稽之谈。但这只是为了演示。 我的问题是,为什么这段代码 works 在 2.1 解决方案中但在 3.1 not 并且出现异常 InvalidOperationException:在前一个操作完成之前在此上下文中启动了第二个操作。(对于 IIS 和 Kestrel 也是如此)。

我知道如何在 3.1 中修复它,这不是我的问题。我只需要知道为什么会发生这种情况以及这些版本之间或任何时候发生了什么变化。

非常感谢您的任何回复。

【问题讨论】:

  • 我不相信 DbContext 在 EF Core 2 或 3 中是线程安全的。因此,您不应该在 Task.WhenAll 的同一上下文中运行两个 personRepository.GetAll() 任务,因为它们 可以同时运行。您应该async/await 2 和 3 中的每个调用。任务调度的问题之一是您不知道它将如何完成。在您的 2 个示例中,这两个任务可能按顺序运行,而在您的 3 个示例中,它们可能同时运行并给您错误。
  • @GrahamKing 感谢您的评论。是的,DBContext 不是线程安全的。所以它可能按照你说的完成,我不知道它是如何完成的(任务调度)。这很有趣,我需要更深入地研究它。

标签: c# .net asp.net-core entity-framework-core asp.net-core-3.1


【解决方案1】:

如果您真的想要并行运行两个查询,则需要两个 DbContexts,因为 DbContext 不是线程安全的。

您需要更改在服务容器中注册 DbContext 的方式才能执行此操作:

 services.AddDbContext<AppDbContext>(options => options
            .UseSqlServer("")
            .EnableSensitiveDataLogging(true), 
            ServiceLifetime.Transient);

为依赖添加创建DbContext 的新实例的能力(一个简单的工厂):

services.AddTransient<Func<AppDbContext>>(provider => provider.GetRequiredService<AppDbContext>);

并相应地改变你的依赖:

public PersonRepository(Func<AppContext> appContextFactory)
{
    this.appContextFactory = appContextFactory;
}

public async Task<IEnumerable<Person>> GetAll()
{
   using (var appContext = appContextFactory())
   {
       return await appContext.People.ToListAsync();
   }
}

请记住,将生命周期范围更改为 Transient 意味着如果您在同一请求中的多个类中注入 DbContext,您将不会获得相同的 DbContext 实例。谨慎使用。

【讨论】:

  • 您好@GrahamKing 先生,我的解决方案与您想象的实现完全相同。我唯一的问题是为什么两个版本之间存在差异。也许正如您所说,“任务调度的一个问题是您不知道它将如何完成。在您的 2 示例中,这两个任务可能按顺序运行,而在您的 3 示例中,它们同时运行并为您提供错误。”。无论如何,非常感谢!
  • 很高兴你让它工作了@T.Dominik。如果您找到上述帮助,也许您可​​以将此标记为答案。 stackoverflow.com/help/someone-answers 对当有人回答您的问题时该怎么做有一些指导。
  • 你好@GrahamKing ...这实际上不能回答我的问题。因为在您发布答案之前我就知道了这个解决方案。正如我在最初的问题中所说(我知道如何让它工作)。对此我感到非常抱歉
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 2023-01-22
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
相关资源
最近更新 更多