I see many questions on Stackoverflow that are basically variants of this: "How can I integrate my custom MembershipProvider into my IoC container?"
Integrating your custom MembershipProvider to a IoC container has many advantages, since it would let you treat it just like any other service: you could manage its lifetime, dependencies, configuration, even proxy it if you wanted.
Problem is, MembershipProviders are one of those things that are managed by the ASP.NET runtime. You just configure it in your web.config and the runtime instantiates it when it's needed. You don't really get much control over its creation.
A cheap solution is to use the container as a service locator directly in your membership provider (using something like CommonServiceLocator), e.g.:
public class MyMembershipProvider : MembershipProvider {
private IUserRepository repo {
get { return ServiceLocator.Current.GetInstance<IUserRepository>(); }
}
public override string GetUserNameByEmail(string email) {
return repo.First(u => u.Email == email);
}
...
}
Using a service locator like this should be avoided as much as possible. Mark Seeman explains it thoroughly in this article. In a nutshell, you want to limit the usage of the service locator pattern to glue code (i.e. very low-level infrastracture), even there use it as little as possible, and never use it in application-level code.
As usual with this kind of problems, the solution is to write a wrapper/adapter/bridge/whatever-you-want-to-call-it that isolates the issue so that client code doesn't have to suffer it. It's similar in concept to the implementation of Windsor-managed HttpModules. It's actually simpler than that, we don't need a custom lifestyle manager here.
In fact, Spring.NET has had such an adapter for quite some time. The only problem with that implementation is that you can't change the lifetime of the custom provider, it's always a singleton. My implementation doesn't have this limitation: your provider can be transient, singleton, per web request, whatever. The price for this is that you can't use Initialize() (more precisely, it won't do anything), but since it's managed by the container, you can use the container to provide any configuration, which is much more flexible. The implementation is about 200 lines of boring, simple code so I'm not going to post it here. It does use Windsor as a service locator, but this is low-level infrastracture/glue code. The goal here is to keep your code clean.
The code is here, and here's how to use it:
- Write your custom MembershipProvider as a regular component, using constructor or property injection as you see fit.
- Implement IContainerAccessor in your global HttpApplication class. Use this article as reference.
- Register your custom provider in Windsor and assign a name to the component. E.g.:
container.Register(Component.For<MyMembershipProvider>()
.LifeStyle.Transient
.Named("myProvider"));
- Register your custom provider in your web.config using the adapter and referencing the name of the corresponding Windsor component in a "providerId" attribute. E.g.:
<membership defaultProvider="customProvider">
<providers>
<clear/>
<add name="customProvider" type="ProviderInjection.WebWindsorMembershipProvider, ProviderInjection" providerId="myProvider"/>
</providers>
</membership>
That's it. Here's a sample app that you can use as reference. This can be easily ported to any IoC container, and for any provider like RoleProvider, ProfileProvider, etc. I haven't used this in anger so let me know if you have any problems with it.