【问题标题】:.NET Core IServiceScopeFactory.CreateScope() vs IServiceProvider.CreateScope() extension.NET Core IServiceScopeFactory.CreateScope() 与 IServiceProvider.CreateScope() 扩展
【发布时间】:2018-10-16 10:23:48
【问题描述】:

我的理解是,当使用内置的依赖注入时,.NET Core 控制台应用程序将要求您自己创建和管理所有范围,而 ASP.NET Core 应用程序将默认创建和管理HttpRequest 范围通过定义的中间件。

使用 ASP.NET Core,您可以选择创建和管理自己的范围,当您需要位于 HttpRequest 之外的服务时,您可以调用 CreateScope()

很明显,每次调用IServiceScopeFactory.CreateScope()都会创建一个新的IServiceScope;但是,是否每次调用IServiceProvider.CreateScope()扩展方法也会创建一个新的IServiceScope

基本上,在 ASP.NET Core 和 .NET Core 控制台应用程序中创建范围的以下方法之间是否存在有意义的区别:

public class Foo()
{
    public Foo(IServiceProvider serviceProvider)
    {
        using(var scope = serviceProvider.CreateScope())
        {   
            scope.ServiceProvider.GetServices<>();           
        }
    }
}

public class Bar()
{
    public Bar(IServiceScopeFactory scopeFactory)
    {
        using(var scope = scopeFactory.CreateScope())
        {   
            scope.ServiceProvider.GetServices<>();           
        }
    }
}

【问题讨论】:

  • IServiceProvider.CreateScope 将解析IServiceScopeFactory 并在其上调用CreateScope。所以在功能上这两种方法是相同的,因为一个调用另一个。
  • 少一行代码是的。 fwiw:除了少数罕见的基础设施案例外,您不必将范围工厂或服务提供者都注入到您的类中
  • 这是有道理的——避免使用服务定位器模式。在我的例子中,注入是针对 IHostedService/BackgroundService 的,它需要它自己的范围与 ASP.NET 默认值分开。这将只是服务的一次注入,所有依赖项都将从StartUp.cs 中的注册中解决。我正在努力注入ApplicationDbContext(范围进入单例=异常)。通过注册一个单例 contextFactory 解决了这个问题。对这种方法有什么想法吗?对吗?
  • CreateScope 还用于任何根应用程序容器,例如 webhost 服务提供者。根应用程序提供者和它创建的所有对象将在应用程序的生命周期中存在。如果我们从根容器实例解析范围或临时服务它们将被创建为单例。相反,通过使用范围,我们可以确保尊重它们的预期生命周期。示例 var host = CreateWebHostBuilder(args).Build();使用 (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; var ds = services.GetService();
  • @CK - 我有一个类似的场景 - 需要异步后台服务。因此,我将我的服务注册为 HostedService,然后使用注入的 IServiceProvider 到 GetRequiredService()。关于某个主题的好文章:docs.microsoft.com/en-us/dotnet/architecture/microservices/…

标签: c# asp.net-core dependency-injection .net-core


【解决方案1】:

CreateScope from IServiceProvider解析IServiceScopeFactory并调用CreateScope()就可以了:

public static IServiceScope CreateScope(this IServiceProvider provider)
{
    return provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}

所以,正如@Evk 所说

两种方法在功能上是相同的

IServiceProvider 刚刚从IServiceScopeFactory 调用CreateScope()

【讨论】:

    【解决方案2】:

    根据我的测试

    在 ASP.NET Core 5 中,以下代码有效:

    [HttpGet("/Echo/{word}")]
    public IActionResult EchoAndLog(string word, [FromServices] IServiceScopeFactory serviceScopeFactory)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress;
    
        // No need to wait for logging, just fire and forget
        Task.Run(async () =>
        {
            await Task.Delay(1000);
    
            using (var scope = serviceScopeFactory.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<LogDbContext>();
    
                var log = new ActivityLog
                {
                    IpAddress = ipAddress,
                    Endpoint = "Echo",
                    Parameter = word
                };
    
                context.Add(log);
                await context.SaveChangesAsync();                                        
            }
        });
    
        return Ok(word);
    }
    

    现在如果您将 IServiceScopeFactory 更改为 IServiceProvider 它将不起作用:

    [HttpGet("/Echo/{word}")]
    public IActionResult EchoAndLog(string word, [FromServices] IServiceProvider serviceProvider)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress;
    
        // No need to wait for logging, just fire and forget
        Task.Run(async () =>
        {
            await Task.Delay(1000);
    
            using (var scope = serviceProvider.CreateScope())
            {
                var context = scope.ServiceProvider.GetRequiredService<LogDbContext>();
    
                var log = new ActivityLog
                {
                    IpAddress = ipAddress,
                    Endpoint = "Echo",
                    Parameter = word
                };
    
                context.Add(log);
                await context.SaveChangesAsync();                                        
            }
        });
    
        return Ok(word);
    }
    

    你会得到System.ObjectDisposedException异常:

    无法访问已处置的对象。

    对象名称:'IServiceProvider'。

    这告诉我IServiceProvider 将与请求的生命周期(范围)一样长,但IServiceScopeFactory 的情况并非如此。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-24
      • 1970-01-01
      • 2017-10-04
      • 2021-03-21
      • 2018-12-14
      • 2020-01-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多