繁体   English   中英

向简单注入器注册IAuthenticationManager

[英]Register IAuthenticationManager with Simple Injector

我正在为Simple Injector进行配置设置,已将所有注册信息移至OWIN管道。

现在的问题是我有一个控制器AccountController ,它实际上将参数作为

public AccountController(
    AngularAppUserManager userManager, 
    AngularAppSignInManager signinManager, 
    IAuthenticationManager authenticationManager)
{
    this._userManager = userManager;
    this._signInManager = signinManager;
    this._authenticationManager = authenticationManager;
}

现在我的Owin Pipeline配置看起来像这样

public void Configure(IAppBuilder app)
{
    _container = new Container();
    ConfigureOwinSecurity(app);
    ConfigureWebApi(app);
    ConfigureSimpleinjector(_container);

    app.Use(async (context, next) =>
    {
        _container.Register<IOwinContext>(() => context);
        await next();
    });

    _container.Register<IAuthenticationManager>(
        () => _container.GetInstance<IOwinContext>().Authentication);

    _container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
}

private static void ConfigureOwinSecurity(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        CookieName = "AppNgCookie",
        //LoginPath = new PathString("/Account/Login")
    });
}

private static void ConfigureWebApi(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();
    WebApiConfig.Register(config);
    app.UseWebApi(config);
}

private static void ConfigureSimpleinjector(Container container)
{
    SimpleInjectorInitializer.Initialize(container);
}

而且简单注入器初始化器看起来像这样

private static void InitializeContainer(Container container)
{
    container.Register<DbContext, AngularAppContext>();

    container.Register<IUserStore<Users, Guid>, AngularAppUserStore>();
    container.Register<IRoleStore<Roles, Guid>, AngularAppRoleStore>();

    container.Register<UserManager<Users, Guid>, AngularAppUserManager>();
    container.Register<RoleManager<Roles, Guid>, AngularAppRoleManager>();
    //container.RegisterPerWebRequest<SignInManager<Users, Guid>, AngularAppSignInManager>();

    container.Register<IdentityFactoryOptions<AngularAppUserManager>, IdentityFactoryOptions<AngularAppUserManager>>();
    //container.Register<IAuthenticationManager>(() => HttpContext.Current.GetOwinContext().Authentication);

    //container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
    // For instance:
    // container.Register<IUserRepository, SqlUserRepository>();
}

现在的问题是,控制器无法注册IAuthenticationManager 我尝试使用

container.Register<IAuthenticationManager>(
    () => HttpContext.Current.GetOwinContext().Authentication);

但这给我留下了如下异常:

System.InvalidOperationException:在上下文中找不到owin.Environment项目。

在这条线

container.Register<IAuthenticationManager>(
    () => HttpContext.Current.GetOwinContext().Authentication);

我也尝试使用HttpContext.Current.GetOwinContext().Authentication而不是在public void Configure(app)方法中使用上述配置进行HttpContext.Current.GetOwinContext().Authentication ,以使用app.Use()进行注册。 然后通过容器解析它,以获取IAuthenticationManager 但是种种可能性使我失败了。

我在这里想念什么? 为什么HttpContext.Current.GetOwinContext().Authentcation无法从OwinContext解析身份验证?

如果不是这样,为什么通过app.Use使用相同的配置也不起作用?

正如TrailMax已经提到的那样,在调用container.Verify()可能会引发异常。 在应用程序启动时,没有HttpContext ,因此是例外。

尽管删除对container.Verify()的调用将“解决”问题,但我建议不要这样做,并在下面提出更好的解决方案。

NightOwl888引用了马克·西曼(Mark Seemann)的一篇老文章(我非常尊重他在DI上的工作)。 Mark在那篇文章中解释了为什么他认为验证容器是无用的。 但是,本文似乎过时,并且与Mark的较新文章相冲突。 马克(Mark)在较新的文章中解释说,使用Pure DI (不使用DI容器的依赖注入)的一大优点是, 它提供了关于正确性的最快反馈 Mark和我们其他人显然都将编译器的反馈和静态代码分析工具的反馈视为快速反馈机制。 Simple Injector的.Verify()Diagnostic Services都试图将这种快速反馈带回来。 我认为,Simple Injector的.Verify()方法承担了编译器在执行Pure DI时将为您执行的工作,而Diagnostic Services在某种意义上是专用于DI配置的静态代码分析工具。

尽管确实不可能对容器进行100%的配置验证,但对我而言,验证仍然是一种宝贵的实践。 认为对.Verify()的简单调用会导致完全无错误甚至是正常的应用程序,这是很愚蠢的。 如果有人可能认为这就是“验证” DI配置的含义,那么我就会理解为什么他们会认为此功能毫无价值。 听起来像是在说实话。 那里没有容器,包括Simple Injector,它伪装成具有这种功能。

当然,您仍然有责任编写集成和/或单元测试,例如,检测所应用装饰器的顺序是否正确,或者是否确实在容器中注册了ISomeService<T>所有实现。

我想在Mark的博客中提到2个反对验证容器的特定论点。

容易进入容器验证的情况,但在运行时仍会中断。

我同意这一点,但是我确实认为Simple Injector文档已对如何在此处实现此方法提供了一些很好的指导。

在通过配置进行约定时,很容易获得无论如何都不应该在容器中的注册。

我从来没有遇到过这个问题,因为我认为无论如何防止这种情况是一种理智的做法。

回到问题:

尽管Simple Injector文档中的技巧之一是使用抽象工厂,但在这种情况下我不会这样做。 为已经存在的东西创建工厂对我来说听起来很奇怪。 也许这只是正确命名的问题,但是AccountController为什么需要AuthenticationFactoryAuthenticationContext 换句话说,由于ASP.NET Identity中的某些设计问题,为什么应用程序应该不知道有关我们在接线方面遇到的问题?

相反,通过调整IAuthenticationManager的注册,我们可以在启动/验证时从新创建的OwinContext返回Authentication组件,并在运行时返回“正常”或已配置的AuthenticationManager 这将消除对工厂的需求,并将责任移到应有的组成根目录。 并允许您将IAuthenticationManager注入到需要的任何位置,同时仍然可以调用.Verify()

代码如下:

container.RegisterPerWebRequest<IAuthenticationManager>(() => 
    AdvancedExtensions.IsVerifying(container) 
        ? new OwinContext(new Dictionary<string, object>()).Authentication 
        : HttpContext.Current.GetOwinContext().Authentication); 

然而,一个甚至更多的SOLID解决方案将是完全不依赖IAuthenticationManager ,因为依赖此接口会导致我们违反接口隔离原则,从而使其难以创建代理实现,从而延迟创建。

您可以通过定义仅适合您的需求的抽象来实现。 查看对IAuthenticationManager的身份模板调用,此抽象只需要.SignIn().SignOut()方法。 但是,这将迫使您完全重构由Visual Studio模板“免费”获得的糟糕的AccountController ,这可能是一项艰巨的任务。

您对IAuthenticationManager注册所做的一切对我来说都没有问题。 在某个时候,我得到的异常与您得到的相同,但这是由于

container.Verify();

在容器配置之后。 它试图创建注册对象的所有实例,但是不存在HttpContext.Current ,因此是例外。

在没有任何HTTP请求可用之前,您是否没有从容器中取出任何实例? 如果您确实需要它们,那么解决此问题的唯一方法是使用NightOwl888建议的Factory。 如果您在HTTP请求之前不需要容器,请进行重构,因此不要与HTTP请求一起使用。

在这里查看我的答案。

尽管您访问的是其他类型,但问题是相同的。 在应用程序启动期间,您不能依赖HttpContext的属性,因为该应用程序是在用户上下文之外初始化的。 解决方案是使抽象工厂在运行时(而不是在对象创建时)读取值,并将工厂而不是IAuthenticationManager类型注入到控制器中。

public class AccountController
{
    private readonly AngularAppUserManager _userManager;
    private readonly AngularAppSignInManager _signInManager;
    private readonly IAuthenticationManagerFactory _authenticationManagerFactory;

    public AccountController(AngularAppUserManager userManager
      , AngularAppSignInManager signinManager
      , IAuthenticationManagerFactory authenticationManagerFactory)
    {
        this._userManager = userManager;
        this._signInManager = signinManager;
        this._authenticationManagerFactory = authenticationManagerFactory;
    }

    private IAuthenticationManager AuthenticationManager
    {
        get { return this._authenticationManagerFactory.Create(); }
    }

    private void DoSomething()
    {
        // Now it is safe to call into HTTP context
        var manager = this.AuthenticationManger;
    }
}

public interface IAuthenticationMangerFactory
{
    IAuthenticationManger Create();
}

public class AuthenticationMangerFactory
{
    public IAuthenticationManger Create()
    {
        HttpContext.Current.GetOwinContext().Authentication;
    }
}

// And register your factory...
container.Register<IAuthenticationManagerFactory, AuthenticationMangerFactory>();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM