代码之家  ›  专栏  ›  技术社区  ›  Yasser Shaikh

如何做DIasp.net核心中间件?

  •  12
  • Yasser Shaikh  · 技术社区  · 6 年前

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

    我的启动.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解决方案(类型serviceType、IServiceScope、IServiceScope rootScope)

    2 回复  |  直到 4 年前
        1
  •  109
  •   Kirk Larkin    5 年前

    UserManager<ApplicationUser> (默认情况下)注册为 依赖,而你的 CreateCompanyMiddleware 中间件是在应用程序启动时构建的(有效地使其成为 单子 ). 这是一个相当标准的错误说,你不能采取 单子

    在这种情况下,解决方法很简单-可以注入 用户管理器<应用程序用户> 进入你的 Invoke 方法:

    public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager)
    {
        await _next.Invoke(context);
    }
    

    这记录在 ASP.NET Core Middleware: Per-request middleware dependencies :

    范围 范围 在中间件和其他类型之间,将这些服务添加到 方法的签名。这个 援引 方法可以接受由DI填充的其他参数:

        2
  •  21
  •   Kirk Larkin    5 年前

    另一种方法是通过 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>();
    
    1. 为什么会这样?

    中间商使用 IMidware软件 是由 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); middlewareType (已注册为服务) ServiceProvider .

    这些实例都是由 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, ...);
    }
    

    援引 方法是每个请求请求的。