Inject a MembershipProvider into ASP.NET MVC AccountController
F

1

7

ASP.NET MVC 1.0 project templates include an AccountController class, which supports constructor injection:

public AccountController(IFormsAuthentication formsAuth, 
    IMembershipService service)
{
    FormsAuth = formsAuth ?? new FormsAuthenticationService();
    MembershipService = service ?? new AccountMembershipService();
}

An AccountMembershipService class is also included, and it too supports constructor injection:

public AccountMembershipService(MembershipProvider provider)
{
    _provider = provider ?? Membership.Provider;
}

I'm sure many of you have used these for unit testing, but my goal is to inject a SqlMembershipProvider using Windsor, and thus configure it at runtime using Windsor XML files rather than web.config. In other words, I want to use constructor injection for the AccountMembershipService class, and I want to continue using the built in ASP.NET 2.0 Membership system. I just want the configuration of the Membership system to go through Windsor IoC.

Is this possible without writing my own MembershipProvider, or does SqlMembershipProvider not play well with IoC?

From MSDN: "The SqlMembershipProvider constructor is called by ASP.NET to create an instance of the SqlMembershipProvider class as specified in the configuration for the application. This constructor is not intended to be used from your code."

I believe Phil asked a very similar question, here are the answers he received.

Thanks for your help.


UPDATE: Just to be clear, my reason for suppling the application's MembershipProvider via DI is to support multiple tenants. Each tenant has an isolated database with ASP membership tables. DI allows me to switch connection strings at runtime and thus keep the core application agnostic about which database is being used for each tenant. Windsor is controlling the DI and it "knows" which tenant makes the request via url:
var url = HttpContext.Current.Request.ServerVariables["HTTP_HOST"]

Mike Hadlow writes about this technique. I am just trying to integrate SqlMembershipProvider into his use of this IoC design.

Frisch answered 28/5, 2009 at 20:6 Comment(7)
One detail that may blow this open: what is Membership.Provider in the constructor for AccountMembershipService?Littlest
.Provider is "The default membership provider for the application exposed using the System.Web.Security.MembershipProvider abstract base class."Frisch
If the AccountMembershipService constructor gets a null provider, Membership.Provider will return the SqlMembershipProvider (instantiated by the runtime) based on web.config settings. That's the behavior I want to replace through Windsor.Frisch
What is your real goal? Having the MembershipProvider injected has nothing to do with having it configured by WindsorDocent
@mausch The app needs multiple tenants, each with an isolated db and membership tables. I'm using Windsor to switch out components based on a context (the URL). SqlMembershipProvider is one I want to switch out: tenant1.example.com and tenant2.example.com would have different css, connectionString, provider etc -- specified in Windsor config. Mike Hadlow explains the technique mikehadlow.blogspot.com/2008/11/…, but I'm having trouble switching out the SqlMembershipProvider since "This constructor is not intended to be used from your code."Frisch
ahhhh ok, now I get it... however you can have multiple membership providers... I'll update my answer.Docent
Please update your question and add this new piece of information.Docent
D
3

Assuming you have your membership providers configured something like this:

<membership>
    <providers>
        <clear/>
        <add name="www.tenant1.com" 
         type="System.Web.Security.SqlMembershipProvider, ..." 
         .../>
        <add name="www.tenant2.com" 
         type="System.Web.Security.SqlMembershipProvider, ..." 
         .../>
    </providers>
</membership>

you can have Windsor select the appropriate provider like this:

var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<MembershipProvider>()
    .LifeStyle.Transient
    .UsingFactoryMethod(() => Membership.Providers[HttpContext.Current.Request.Url.Host]));
... (your controller registrations, etc)
Docent answered 29/5, 2009 at 1:56 Comment(1)
Worked like a charm! Thank you mausch.Frisch

© 2022 - 2024 — McMap. All rights reserved.