Unfortunately, ASP.NET providers like the MembershipProvider and RolesProvider aren’t designed for dependency injection. Many users have problems to assign dependencies to them. In this blog post I’ll show a way to how this can be done using Ninject.Web.Common 3.0.0. This solution works for MVC and WebForms.
The provider itself is created by the ASP.NET framework which adds the restriction that all dependencies have to be passed using property injection because the ASP.NET framework expects a parameter less constructor. So the first thing you have to do is to add a property for all required dependencies to your provider
public class MyMembershipProvider : SqlMembershipProvider { [Inject] public SpecialUserProvider SpecialUserProvider { get; set; } public override bool ValidateUser(string username, string password) { return this.SpecialUserProvider.IsSpecialUser(username) || base.ValidateUser(username, password); } }
As second step you need to create an HttpModule that will get an instance of all your providers. The idea behind this is that the providers are resolved and injected long time before they are used somewhere else. In the following example it is done for the MembershipProvider only. But you can easily extend it to initialize all required providers.
public class ProviderInitializationHttpModule : IHttpModule { public ProviderInitializationHttpModule(MembershipProvider membershipProvider) { } public void Init(HttpApplication context) { } public void Dispose() { } }
As last step you need to create the bindings for the http module and the providers.
kernel.Bind<MembershipProvider>().ToMethod(ctx => Membership.Provider); kernel.Bind<IHttpModule>().To<ProviderInitializationHttpModule>();
I don’t think i’m following this. Can you elaborate a bit?
What is the sequence of events?
When I call Membership.ValidateUser(), if the Membership provider is not yet loaded, ASP.NET goes out and loads it. Then what happens given the above?
I am also following your post and trying to figure out where I need to put your ProviderInitializationHttpModule. I am using the NinjectMVC3 class created by NuGet (using Ninject 2.2) and have the following code in my domain project. Your ProviderInitializationHttpModule though requires a Http Context, so that sounds like it should be in my web project. Just trying to figure out where to put this.
public class MyMembershipProvider : MembershipProvider
{
[Inject]
private IUnitOfWork UnitOfWork { get; set; }
private readonly IRepository _memberRepository;
public MyMembershipProvider()
{
_memberRepository = UnitOfWork.RepositoryFor();
}
//… more methods here …
}
@Erik
The membership provider will be created together with the HttpApplication in the global.asax. This is long before you access the MembershipProvider.
@Brian
As the title states, this approach will work with Ninject 3.0.0. Ninject 3.0.0 added support for injection of HttpModules by adding a binding for them instead of adding a configuration to the web.config.
@remo.gloor Thanks… I’ll upgrade to the latest version. However, my one question still remains (and please excuse my naivete, I’m still learning MVC). Where does your ProviderInitializationHttpModule go? I assume it does not go in my domain project where my provider lives, correct? Do I just put it anywhere in my web project?
Since this is a kind of application initialization code, I’d put it where you set up Ninject.
I haven’t done any digging into the source — is thread safety maintained?
@Adam Schaible
This all happens during application startup. AFAIK, there is just one thread used by IIS at that time. So there shouldn’t be any thread safety issues. You can also use InSingletonScope for the provider binding which will ensure that all it is threadsafe.
Well, for example – if I bind my DataContext into a provider – I want that to be request scoped. Since providers are essentially singletons, are bindings into providers required to be Singleton as well? If it is only injected once, then it will be disposed after the first request.
Providers are always singletons no matter what scope you use because they use the singleton pattern internally. You can never use dependencies that have a shorter lifecycle. E.g. using a request scoped dependency inside a singleton because you will end up referencing disposed objects. This means you have to do a thread safe implementation of the provider. What you can do though is to inject a factory into the provider and create a new request scoped handler for each request.
Yeah – that’s what I figured. I was hoping that perhaps you had found a way to instance encapsulate an instance or something that would play nice with scopes.
IMO the best thing to do is just inject the kernel. In my situation, the factory approach just adds unnecessary scaffolding and another mock for tests. I don’t really see anything wrong with injecting the kernel here — would you agree?
Hi Remo, I’m a having a trouble injecting a Repository in my custom RoleProvider, I’m getting the error “The operation cannot be completed because the DbContext has been disposed.” when the DbContext object is being used.
I overrode the Dispose method of the DbContext like this:
protected override void Dispose(bool disposing) { }
and it works out, but I do not know if it is a good practice and how it is going to affect the performance of my site, or if there is a better way to work around on this.
Please, see the rest of basic code below:
//my custom RoleProvider
public class MyCERoleProvider : RoleProvider {
//the repository I am trying to inject
[Inject]
public UserRepository Repository { get; set; }
//I get the error here because it calls a Repository’s function that tries to use the DbContext object wich is disposed at this time (property: IsDisposed = true).
public override string[] GetRolesForUser(string username) {
return Repository.GetRolesFor(username);
}
}
//HttpModule for enabling the injection
public class RoleProviderHttpModule : IHttpModule {
public RoleProviderHttpModule(RoleProvider roleProvider) { }
public void Init(HttpApplication context) { }
public void Dispose() { }
}
//repository basics
public class UserRepository : EFRepository {
public UserRepository(DbContext context) : base(context) { }
}
//repository’s parent constructor
public class EFRepository : IRepository where TEntity : class {
protected DbContext dbContext;
internal DbSet dbSet;
public EFRepository(DbContext context) {
if (context == null) { throw new ArgumentNullException(“context”); }
this.dbContext = context;
this.dbSet = this.dbContext.Set();
}
}
//NinjectWebCommon’s binding method
//MyCEContext is my extension for DbContext class.
private static void RegisterServices(IKernel kernel) {
kernel.Bind().ToSelf().InRequestScope();
kernel.Bind().ToMethod(ctx => ctx.Kernel.Get());
kernel.Bind(typeof(IRepository)).To(typeof(EFRepository));
kernel.Bind().ToMethod(ctx => Roles.Provider);
kernel.Bind().To();
}
Thanks !
This is the same problem as I mentioned in my last comment.
Remo, how can I inject a factory in a provider (e.g. RoleProvider) since I have to use the parameterless constructor for this class ?? can you give me an example ?
Thanks !