为什么 Scoped 服务解析为同一请求的两个不同实例? [英] Why does Scoped service resolve as two different instances for same request?

查看:26
本文介绍了为什么 Scoped 服务解析为同一请求的两个不同实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的服务,其中包含一个 List.在 Startup.cs 中,我使用了 services.addScoped() 方法.

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.

即使我在控制器操作的列表中添加了一个 Foo,中间件中的 Foo 列表始终是空的.这是为什么?

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?

我尝试使用 AddSingleton() 将服务注册更改为单例,并且它按预期工作.但是,这必须限于当前请求.非常感谢任何帮助或想法!

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

【下面是我注入服务的两个地方,产生了两个不同的实例】

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

}

推荐答案

那是因为当你将 IServiceProvider 注入你的中间件时 - 那是全局"提供者,而不是请求范围的.调用中间件构造函数时没有请求(中间件在启动时创建一次),因此它不能是请求范围的容器.

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.

请求开始时,会创建新的 DI 作用域,并使用与此作用域相关的 IServiceProvider 来解析服务,包括将服务注入您的控制器.所以你的控制器从请求范围解析 FooService (因为注入到构造函数),但你的中间件从父"服务提供者(根范围)解析它,所以它是不同的.解决此问题的一种方法是使用 HttpContext.RequestServices:

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

但更好的方法是将其注入到 Invoke 方法本身,然后它将成为请求范围:

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

这篇关于为什么 Scoped 服务解析为同一请求的两个不同实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆