Why does Scoped service resolve as two different instances for same request?

时间秒杀一切 提交于 2019-12-05 04:00:18

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 resolves FooService 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 use HttpContext.RequestServices:

public async Task Invoke(HttpContext context)
{
    context.Response.OnStarting(() =>
    {
        var fooService = context.RequestServices.GetService(typeof(FooService)) as FooService;

        var fooCount = fooService.Foos.Count; // always equals zero

        return Task.CompletedTask;
    });

    await this.next(context);    
}

But even better way is to inject it into Invoke method itself, then it will be request scoped:

public async Task Invoke(HttpContext context, FooService fooService)
{
    context.Response.OnStarting(() =>
    {    
        var fooCount = fooService.Foos.Count; // always equals zero

        return Task.CompletedTask;
    });

    await this.next(context);    
}

First of all you shouldn't be using GetService, use the proper DI system that is in place by passing it into the Invoke 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.

public class AppMessageMiddleware
{
    private readonly RequestDelegate _next;

    public AppMessageMiddleware(RequestDelegate next, IServiceProvider serviceProvider)
    {
        _next = next;
    }

    //Note the new parameter here:                vvvvvvvvvvvvvvvvvvvvv
    public async Task Invoke(HttpContext context, FooService fooService)
    {
        context.Response.OnStarting(() =>
        {
            var fooCount = fooService.Foos.Count;

            return Task.CompletedTask;
        });

        await _next(context);

    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!