.NET Core IServiceScopeFactory.CreateScope()vs IServiceProvider.CreateScope()扩展

Con*_*ing 23 c# dependency-injection .net-core asp.net-core

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

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

很明显,每次呼叫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<>();           
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Rom*_*syk 20

来自IServiceProvider的CreateScope解析IServiceScopeFactory并调用CreateScope()它:

public static IServiceScope CreateScope(this IServiceProvider provider)
{
    return provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}
Run Code Online (Sandbox Code Playgroud)

所以,正如@Evk所说

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

IServiceProvider刚刚结束通话CreateScope()距离IServiceScopeFactory


Sas*_*san 16

从我测试的

在 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);
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您更改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);
}
Run Code Online (Sandbox Code Playgroud)

你会得到System.ObjectDisposedException异常:

无法访问已处置的对象。

对象名称:'IServiceProvider'。

这告诉我IServiceProvider将与请求的生命周期(范围)一样长,但IServiceScopeFactory.

  • `IServiceProvider` 是一个作用域服务,而 `IServiceScopeFactory` 是一个单例。我可以想象,在“Task.Run”中的代码开始执行之前,请求完成并且相应的范围以及作为参数传递的“IServiceProvider”实例被销毁。让任务在后台执行似乎很可疑。 (3认同)

Rod*_*voi 9

简而言之,IServiceProvider.CreateScope()IServiceScopeFactory.CreateScope()是相同的(在非作用域上下文中,甚至IServiceProvider和的实例IServiceScopeFactory也是相同的)。

但这些抽象之间有一点区别

IServiceProvider的一生都可以 Scoped。但IServiceScopeFactory人的一生总是如此Singleton

什么时候IServiceProvider确定范围?

当我们将其注入到作用域服务或方法中时。所以,当我们注入IServiceProvider控制器时,可以在不创建作用域的情况下解决作用域依赖关系(因为在这种情况下作用域已经创建)。

例子

因此,在下面的示例中,您不需要创建作用域来在控制器的任何操作中解析(检索)作用域服务:

public class SomeController : ControllerBase
{
    private readonly IServiceProvider _serviceProvider;

    public SomeController(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IActionResult SomeAction()
    {
        //Resolve a scoped service from IServiceProvider without creating a scope:
        var service = _serviceProvider.GetRequiredService<ScopedService>();
        return Ok(service.GetHashCode());
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我们注入IServiceProvider单例时,它将成为根提供者(无法获取范围服务)。
这是我准备的更多示例。

请注意,您应该避免使用IServiceProvider或 ,IServiceScopeFactory因为它们都实现了称为Service Locator 的反模式