.NET 性能最佳做法:请勿在后台线程上捕获注入到控制器中的服务

请勿这样做:下面的示例演示闭包从 Controller 操作参数捕获 。这是一种不良做法。工作项可能在请求范围之外运行。ContosoDbContext 的范围限定为请求,从而导致 ObjectDisposedException。.
[HttpGet("/fire-and-forget-1")]public IActionResult FireAndForget1([FromServices]ContosoDbContext context){    _ = Task.Run(async () =>    {        await Task.Delay(1000);
        context.Contoso.Add(new Contoso());        await context.SaveChangesAsync();    });
    return Accepted();}

请这样做:下面的示例:

  • 注入 IServiceScopeFactory 以便在后台工作项中创建范围。IServiceScopeFactory 是单一实例。

  • 在后台线程中创建新的依赖项注入范围。

  • 不从控制器引用任何内容。

  • 不从传入请求捕获 ContosoDbContext。

[HttpGet("/fire-and-forget-3")]public IActionResult FireAndForget3([FromServices]IServiceScopeFactory                                     serviceScopeFactory){    _ = Task.Run(async () =>    {        await Task.Delay(1000);
        using (var scope = serviceScopeFactory.CreateScope())        {            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();
            context.Contoso.Add(new Contoso());
            await context.SaveChangesAsync();                                                }    });
    return Accepted();}
以下突出显示的代码:
  • 在后台操作生存期内创建范围,并解析其中的服务。

  • 从正确的范围使用 ContosoDbContext。

[HttpGet("/fire-and-forget-3")]public IActionResult FireAndForget3([FromServices]IServiceScopeFactory                                     serviceScopeFactory){    _ = Task.Run(async () =>    {        await Task.Delay(1000);
        using (var scope = serviceScopeFactory.CreateScope())        {            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();
            context.Contoso.Add(new Contoso());
            await context.SaveChangesAsync();                                                }    });
    return Accepted();}