I have a simple service that contains a List<Foo>
. In Startup.cs, I am using the services.addScoped<Foo, Foo>()
method.
I am inject the service instance in two different places (controller and middleware), and for a single request, I would expect to get the same instance. However, this does not appear to be happening.
Even though I am adding a Foo to the List in the Controller Action, the Foo list in the Middleware is always empty. Why is this?
I have tried changing the service registration to a singleton, using AddSingleton()
and it works as expected. However, this has to be scoped to the current request. Any help or ideas are greatly appreciated!
FooService.cs
public class FooService
{
public List<Foo> Foos = new List<Foo>();
}
Startup.cs
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<FooService, FooService>();
}
[Below are the two places where I am injecting the service, resulting in two different instances]
MyController.cs
public class MyController : Controller
{
public MyController(FooService fooService)
{
this.fooService = fooService;
}
[HttpPost]
public void TestAddFoo()
{
//add foo to List
this.fooService.Foos.Add(new Foo());
}
}
FooMiddleware.cs
public AppMessageMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
{
this.next = next;
this.serviceProvider = serviceProvider;
}
public async Task Invoke(HttpContext context)
{
context.Response.OnStarting(() =>
{
var fooService = this.serviceProvider.GetService(typeof(FooService)) as FooService;
var fooCount = fooService.Foos.Count; // always equals zero
return Task.CompletedTask;
});
await this.next(context);
}
That's because when you inject
IServiceProvider
into your middleware - that's "global" provider, not request-scoped. There is no request when your middleware constructor is invoked (middleware is created once at startup), so it cannot be request-scoped container.When request starts, new DI scope is created, and
IServiceProvider
related to this scope is used to resolve services, including injection of services into your controllers. So your controller resolvesFooService
from request scope (because injected to constructor), but your middleware resolves it from "parent" service provider (root scope), so it's different. One way to fix this is to useHttpContext.RequestServices
:But even better way is to inject it into
Invoke
method itself, then it will be request scoped:First of all you shouldn't be using
GetService
, use the proper DI system that is in place by passing it into theInvoke
method as a parameter.Secondly, the reason you are getting a different object is because the constructor of the middleware is called outside of the scope of any request, during the app initialisation phase. So the container used there is the global provider. See here for a good discussion.