为什么Scoped服务解析为同一请求的两个不同实例?
我有一个包含List
的简单服务。 在Startup.cs中,我使用的是services.addScoped()
方法。
我在两个不同的地方(控制器和中间件)注入服务实例,对于单个请求,我希望获得相同的实例。 但是,这似乎并没有发生。
即使我在Controller Action中向List添加Foo,中间件中的Foo列表也始终为空。 为什么是这样?
我已经尝试使用AddSingleton()
将服务注册更改为单例,并且它按预期工作。 但是,这必须限制在当前请求范围内。 非常感谢任何帮助或想法!
FooService.cs
public class FooService { public List Foos = new List(); }
Startup.cs
... public void ConfigureServices(IServiceCollection services) { ... services.AddScoped(); }
[以下是我注入服务的两个地方,导致两个不同的实例]
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
注入你的中间件时 – 那是“全局”提供者,而不是请求范围。 调用中间件构造函数时没有请求(中间件在启动时创建一次),因此它不能是请求范围的容器。
请求启动时,将创建新的DI范围,并且与此范围相关的IServiceProvider
用于解析服务,包括将服务注入控制器。 所以你的控制器从请求范围解析FooService
(因为注入构造函数),但你的中间件从“父”服务提供者(根范围)解析它,所以它是不同的。 解决此问题的一种方法是使用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
方法本身,然后它将是请求作用域:
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); }
首先,您不应该使用GetService
,通过将其作为参数传递给Invoke
方法来使用适当的DI系统。
其次,您获得不同对象的原因是因为在应用程序初始化阶段,中间件的构造函数在任何请求的范围之外被调用。 因此,那里使用的容器是全球提供商。 请参阅此处以获得良好的讨论。
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); } }
- 如何让.NET Core项目复制NuGet引用来构建输出?
- 从.NET Core / ASP.NET Core中的类库访问App密钥数据
- .NET Core 2.0日志记录是否已损坏?
- .NET Core 2.0 RSA PlatformNotSupportedException
- entity framework核心忽略.Include(..)而没有.ToList(..)间接
- 使用UseRequestLocalization强制所有请求使用单个区域性
- 如何在.NET Core中修改文件访问控制
- .net核心dependency injection是否支持Lazy
- .NET Core 2.1 – 循环中的正则表达式200x比2.0慢(简单基准中为3x)