The results of my research :
Windsor
(unlikeStructureMap
) does not have a way of injecting properties into existing objects- The
AuthorizeAttribute
does not call into the RoleProvider implementation directly, it calls intoThread.CurrentPrincipal
which returns anIPrincipal
implementation... - Basically it's impossible to provide a resolved
RoleProvider
to theAuthorizeAttribute
, even though you can get hold if it before it calls the provider by writing your own implementation ofIFilterProvider
(cf. IFilterProvider and separation of concerns)
What I ended up doing was providing a method on the CustomRoleProvider
which set the connection string and log path, then just setting it up in 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>());
}
There might be a better way to do this, but for the moment I went with the pragmatic solution.
Update 2014-02-12
I found another way to replace the provider in the Roles class via refelection. All you need in your web config file is <roleManager enabled="true" />
and to override Name
in your CustomRoleProvider
to return "CustomRoleProvider"
and then in the Application_Start
method add this :
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);
}
The call to Roles.Provider
forces the Roles
class to do its initialisation magic (otherwise we'd have to set a ton of other private fields) and we need to replace the collection because consumers of the Roles class call into that looking for the appropriate provider.
However, I'm not sure that I'd necessarily recommend this and I'm not sure if this is entirely sufficient, but it seems to be working for me so far.
Update 2014-02-20
There's a different way to do this which doesn't involve magical reflection - http://bugsquash.blogspot.co.uk/2010/11/windsor-managed-membershipproviders.html
Although this blog post is talking about the MembershipProvider
, it's fairly trivial to change the code for a RoleProvider
instead. Essentially this is using a kind of adapter pattern leaving the resolution of the actual role provider up to the adapter. It does involve making the container static and using it in a kind of service locator way, but that's a small trade-off I think.