如何使用Windsor将依赖项注入自定义RoleProvider?

时间:2014-02-10 16:16:13

标签: c# asp.net-mvc-4 castle-windsor

我正在使用Windsor和ASP.NET MVC4,我在遗留安全框架周围编写了一个自定义RoleProvider。我需要将一个连接字符串和文件路径注入到提供程序中,这样我就可以将它们提供给遗留框架,但是当我来使用AuthorizeAttribute时,我意识到我不知道如何拦截构造提供者以注入这些值。

如果包含代码有帮助,我的角色提供者就有这种构造函数:

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的依赖注入一样。

这可能吗?或者我是否需要查看另一种提供这些依赖关系的方法?

2 个答案:

答案 0 :(得分:1)

我的研究结果:

  • Windsor(与StructureMap不同)可以将属性注入现有对象
  • AuthorizeAttribute不直接调用RoleProvider实现,它调用Thread.CurrentPrincipal,返回IPrincipal实现...
  • 基本上不可能向RoleProvider提供已解决的AuthorizeAttribute,即使您可以通过编写自己的IFilterProvider实施方案来调用提供商之前获得解决方案(参见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-02-12

我找到了另一种通过refelection替换Roles类中的提供程序的方法。您在网络配置文件中只需要<roleManager enabled="true" />并覆盖Name中的CustomRoleProvider以返回"CustomRoleProvider",然后在Application_Start方法中添加以下内容:< / p>

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-02-20

有一种不同的做法,不涉及魔法反思 - http://bugsquash.blogspot.co.uk/2010/11/windsor-managed-membershipproviders.html

虽然这篇博客文章讨论的是MembershipProvider,但更改RoleProvider的代码却相当简单。本质上,这是使用一种适配器模式,将实际角色提供程序的分辨率保留到适配器。它确实涉及使容器静态并以一种服务定位方式使用它,但我认为这是一个小小的权衡。

答案 1 :(得分:0)

古老的问题,但这在2019年的Asp.Net RoleProvider中对我来说很好。

在您的DI容器配置中

{
    ... Type registrations
    GlobalConfiguration.Configuration.DependencyResolver = 
        new UnityDependencyResolver(container);
}   

在您的RoleProvider中...

private IYourType _yourType;
public override void Initialize(string name, NameValueCollection config)
{
    ...
     _yourType = GlobalConfiguration
                 .Configuration
                 .DependencyResolver
                 .GetService(typeof(IYourType)) as IYourType;