asp.net核心中间件怎么做DI?
Posted
技术标签:
【中文标题】asp.net核心中间件怎么做DI?【英文标题】:How to do DI in asp.net core middleware? 【发布时间】:2019-02-11 17:08:40 【问题描述】:我正在尝试将依赖项注入到我的中间件构造函数中,如下所示
public class CreateCompanyMiddleware
private readonly RequestDelegate _next;
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddleware(RequestDelegate next
, UserManager<ApplicationUser> userManager
)
_next = next;
public async Task Invoke(HttpContext context)
await _next.Invoke(context);
我的 Startup.cs 文件看起来像
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<ApplicationDbContext>(options =>
options.Usemysql(Configuration.GetConnectionString("IdentityConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
app.UseMiddleware<CreateCompanyMiddleware>();
...
但是我收到了这个错误
启动应用程序时出错。 InvalidOperationException:无法从根提供程序解析范围服务“Microsoft.AspNetCore.Identity.UserManager`1[Common.Models.ApplicationUser]”。 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
【问题讨论】:
【参考方案1】:UserManager<ApplicationUser>
(默认情况下)注册为 scoped 依赖项,而您的 CreateCompanyMiddleware
中间件是在应用程序启动时构建的(实际上使其成为 singleton)。这是一个相当标准的错误,表示您不能将 scoped 依赖项放入 singleton 类中。
在这种情况下修复很简单 - 您可以将 UserManager<ApplicationUser>
注入到您的 Invoke
方法中:
public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
await _next.Invoke(context);
这记录在ASP.NET Core Middleware: Per-request middleware dependencies:
因为中间件是在应用启动时构建的,而不是按请求构建的,所以中间件构造函数使用的 范围 生命周期服务不会在每个请求期间与其他依赖注入类型共享。如果您必须在中间件和其他类型之间共享 scoped 服务,请将这些服务添加到
Invoke
方法的签名中。Invoke
方法可以接受由 DI 填充的附加参数:
【讨论】:
对我有用的好简单的解决方案。请注意,如果您忘记在 startup.cs 中添加 DI 类,则中间件中的错误不会总是出现在屏幕上,并且断点也不会被命中。看起来代码正在运行,但可能不是。【参考方案2】:另一种方法是通过IMiddleware
接口创建一个中间件并将其注册为服务
例如中间件
public class CreateCompanyMiddlewareByInterface : IMiddleware
private readonly UserManager<ApplicationUser> _userManager;
public CreateCompanyMiddlewareByInterface(UserManager<ApplicationUser> userManager )
this._userManager = userManager;
public Task InvokeAsync(HttpContext context, RequestDelegate next)
return next(context);
和服务注册:
services.AddScoped<CreateCompanyMiddlewareByInterface>();
-
那么为什么会这样呢?
使用IMiddleware
的中间件由UseMiddlewareInterface(appBuilder, middlewareType type)
构建:
private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
return app.Use(next =>
return async context =>
var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory));
if (middlewareFactory == null) /* throw ... */
var middleware = middlewareFactory.Create(middlewareType);
if (middleware == null) /* throw ... */
try
await middleware.InvokeAsync(context, next);
finally
middlewareFactory.Release(middleware);
;
);
context=>
中的代码是按请求执行的。所以每次有传入请求时,var middleware = middlewareFactory.Create(middlewareType);
将被执行,然后从ServiceProvider
请求middlewareType
的中间件(已注册为服务)。
至于约定俗成的中间件,没有工厂创建它们。
这些实例都是由ActivatorUtilities.CreateInstance()
在启动时创建的。以及任何按约定中间件的Invoke
方法,如
Task Invoke(HttpContext context,UserManager<ApplicationUser> userManage, ILoggerFactory loggeryFactory , ... )
将被编译成如下函数:
Task Invoke(Middleware instance, HttpContext httpContext, IServiceprovider provider)
var useManager /* = get service from service provider */ ;
var log = /* = get service from service provider */ ;
// ...
return instance.Invoke(httpContext,userManager,log, ...);
如你所见,这里的实例是在启动时创建的,Invoke
方法的那些服务是根据请求请求的。
【讨论】:
这里描述了工厂方法:docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/…以上是关于asp.net核心中间件怎么做DI?的主要内容,如果未能解决你的问题,请参考以下文章
asp.net核心中间件中Map和MapWhen分支的区别?
csharp RequestCounter自定义中间件asp.net核心