Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Core IServiceScopeFactory.CreateScope() vs IServiceProvider.CreateScope() extension

My understanding is that when using the built in the dependency injection, a .NET Core console app will require you to create and manage all scopes yourself whereas a ASP.NET Core app will create and manage the HttpRequest scope by default through defined middleware(s).

With ASP.NET Core, you can optionally create and manage your own scopes that by calling CreateScope() for when you need services that live outside of a HttpRequest.

It is clear that calling IServiceScopeFactory.CreateScope() will create a new IServiceScope every time; however, does calling the IServiceProvider.CreateScope() extension method also create a new IServiceScope every time?

Basically, is there a meaningful difference between the following ways to create scope in both ASP.NET Core and .NET Core console apps:

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

and

public class Bar() {     public Bar(IServiceScopeFactory scopeFactory)     {         using(var scope = scopeFactory.CreateScope())         {                scope.ServiceProvider.GetServices<>();                    }     } } 
like image 832
Connie King Avatar asked May 06 '18 13:05

Connie King


People also ask

What does CreateScope do?

With ASP.NET Core, you can optionally create and manage your own scopes that by calling CreateScope() for when you need services that live outside of a HttpRequest . It is clear that calling IServiceScopeFactory. CreateScope() will create a new IServiceScope every time; however, does calling the IServiceProvider.

What is IServiceProvider .NET Core?

The IServiceProvider is responsible for resolving instances of types at runtime, as required by the application. These instances can be injected into other services resolved from the same dependency injection container. The ServiceProvider ensures that resolved services live for the expected lifetime.

Is IServiceScopeFactory a singleton?

This documentation implies that the IServiceScopeFactory that is provided by request services is a singleton, or at least that it always provides service scopes that come from the root application container rather than being hierarchical.

How do you consume scoped service from singleton?

To be able to use scoped services within a singleton, you must create a scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service (the IServiceScopeFactory is itself a singleton, which is why this works).


2 Answers

CreateScope from IServiceProvider resolve IServiceScopeFactory and call CreateScope() on it:

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

So, as said @Evk

functionally both methods are identical

IServiceProvider just wrapped call CreateScope() from IServiceScopeFactory

like image 58
Roman Marusyk Avatar answered Sep 18 '22 09:09

Roman Marusyk


From what I tested

In ASP.NET Core 5 the following code works:

[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); } 

Now if you change the IServiceScopeFactory to IServiceProvider it will NOT work:

[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); } 

You will get the System.ObjectDisposedException exception:

Cannot access a disposed object.

Object name: 'IServiceProvider'.

Which tells me the IServiceProvider will live as long as the request's lifetime (scoped), but this is not the case with IServiceScopeFactory.

like image 45
Sasan Avatar answered Sep 20 '22 09:09

Sasan