2014-02-10 20 views
2

我使用温莎与ASP.NET MVC4和我已经写了一个自定义RoleProvider围绕遗留安全框架。我需要将连接字符串和文件路径注入提供程序,以便我可以将这些提供给传统框架,但是当我使用AuthorizeAttribute时,我意识到我不知道如何拦截提供程序的构造以便注入这些值。如何使用Windsor将依赖注入到自定义RoleProvider中?

如果它有助于包括代码,我的角色提供有这种构造的:

public class CustomRoleProvider : RoleProvider 
{ 
    public CustomRoleProvider(string connectionString, string logPath) 
    { 
     LegacySecurity.ConnectionString = connectionString; 
     LegacySecurity.LogPath = logPath; 
    } 

    [Method overrides go here...] 
} 

AppSettingsConvention看起来是这样的:

public class AppSettingsConvention : ISubDependencyResolver 
{ 
    public bool CanResolve(CreationContext context, 
          ISubDependencyResolver contextHandlerResolver, 
          ComponentModel model, 
          DependencyModel dependency) 
    { 
     return ConfigurationManager.AppSettings.AllKeys.Contains(dependency.DependencyKey) 
       && TypeDescriptor.GetConverter(dependency.TargetType).CanConvertFrom(typeof (string)); 
    } 

    public object Resolve(CreationContext context, 
          ISubDependencyResolver contextHandlerResolver, 
          ComponentModel model, 
          DependencyModel dependency) 
    { 
     return TypeDescriptor.GetConverter(dependency.TargetType) 
          .ConvertFrom(ConfigurationManager.AppSettings[dependency.DependencyKey]); 
    } 
} 

(从这里原来:http://blog.ploeh.dk/2012/07/02/PrimitiveDependencies/

我希望我可以以某种方式替换其中一项服务,就像我将HttpControllerActivator替换为使用依赖项注入一样与ApiController s。

这可能吗?还是我需要看看提供这些依赖关系的另一种方式?

+0

看看这个相关的问题:https://stackoverflow.com/questions/15410575/custom-membership-provider-and-unity-injection – Steven

+0

对不起,我真的不知道这是如何帮助我直接。它给了我实现我自己的'IDependencyResolver'的想法,这可能工作,我猜? – adhocgeek

+0

问题是,你不能在你的'CustomRoleProvider'上进行依赖注入(就像你无法使用成员提供者一样),因为它是基于某些XML配置创建该类型的.NET框架。所以诀窍是创建一个你用XML配置的外观,它解析了容器中的'真实'提供者。这基本上是相关问题描述的内容。 – Steven

回答

1

我的研究结果:

  • Windsor(不像StructureMap)不有注入性到现有的对象
  • AuthorizeAttribute不调入直接RoleProvider实现,它调用的方法到Thread.CurrentPrincipal返回一个IPrincipal执行...
  • 基本上不可能提供解决RoleProviderAuthorizeAttribute,尽管你可以通过编写自己的IFilterProvider(cf. IFilterProvider and separation of concerns

我最终什么事做的是提供其上设置连接字符串和日志路径CustomRoleProvider的方法,那么就设置它在Application_Start

protected void Application_Start() 
{ 
    AreaRegistration.RegisterAllAreas(); 
    WebApiConfig.Register(GlobalConfiguration.Configuration); 
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
    RouteConfig.RegisterRoutes(RouteTable.Routes); 
    BundleConfig.RegisterBundles(BundleTable.Bundles); 

    var provider = Roles.Provider as CustomRoleProvider; 
    if (provider != null) provider.Initialize(ConfigurationManager.AppSettings[ConnectionKey], ConfigurationManager.AppSettings[LogPathKey]); 
    GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), _container.Resolve<IHttpControllerActivator>()); 
    ControllerBuilder.Current.SetControllerFactory(_container.Resolve<IControllerFactory>()); 
} 

有可能是一个更好的办法要做到这一点,但目前我采取了务实的解决方案。

更新2014年2月12日

我找到了另一种方式通过refelection替换角色类的供应商。所有您在Web配置文件需要的是<roleManager enabled="true" />并在CustomRoleProvider覆盖Name返回"CustomRoleProvider"然后在Application_Start方法补充一点:

if (!(Roles.Provider is CustomRoleProvider)) 
{ 
    var rolesType = typeof (Roles); 
    var flags = BindingFlags.Static | BindingFlags.NonPublic; 
    var provider = _container.Resolve<CustomRoleProvider>(); 
    rolesType.GetField("s_Provider", flags).SetValue(null, provider); 
    var providers = new RoleProviderCollection(); 
    providers.Add(provider); 
    rolesType.GetField("s_Providers", flags).SetValue(null, providers); 
} 

Roles.Provider呼叫迫使Roles类做它的初始化魔术(否则我们不得不设置大量的其他专用字段),并且我们需要替换集合,因为Roles类的消费者调用该集合以查找适当的提供者。

但是,我不确定我是否一定会推荐这个,我不确定这是否完全够用,但它似乎对我来说至今还在工作。

更新2014年2月20日

有不同的方式做到这一点不涉及神奇的反射 - 虽然这个博客帖子在谈论MembershipProviderhttp://bugsquash.blogspot.co.uk/2010/11/windsor-managed-membershipproviders.html

,这是相当琐碎改为改为RoleProvider的代码。实质上,这是使用一种适配器模式,将实际角色提供者的解析度留给适配器。它确实涉及到使容器静态化,并以一种服务定位器的方式使用它,但我认为这是一个小小的折衷。

相关问题