in·dom·i·ta·ble
adj.   Incapable of being overcome, subdued, or vanquished; unconquerable.

9th
MAR

Forms Authentication in Asp.Net MVC, Part IV

Posted by indomitablehef | Filed under Asp.Net MVC

This is part 4 of 4
Forms Authentication in Asp.Net MVC

In Parts I, II, and III, you may have noticed constructors in my classes that look like this:

 public class MembershipController: Controller
    {
        private readonly IAuthenticationProvider authenticationProvider;
        private readonly IMembershipProvider membershipProvider;
 
        public MembershipController(IAuthenticationProvider authenticationProvider, IMembershipProvider membershipProvider)
        {
            this.authenticationProvider = authenticationProvider;
            this.membershipProvider = membershipProvider;
        }
 
        public MembershipController():this(ServiceLocator.Current.GetInstance<IAuthenticationProvider>(),
                                            ServiceLocator.Current.GetInstance<IMembershipProvider>()) {}
 
<...>

So what’s all that “ServiceLocator” stuff? That’s a hook int our Inversion of Control container, which happens to be Castle Windsor. If you’re not familiar with IoC, I suggest you check out these dimecasts on the subject. Getting into all the details is outside the scope of this series, but I will show you how to hook it up in Asp.Net MVC.

We’re also using the CommonServiceLocator from the Microsoft Patterns and Practices team (http://www.codeplex.com/CommonServiceLocator) This allows us to use Castle Windsor without making our code dependent on it, such that we could later switch it out for StructureMap, NInject, or any other IoC container that has a ServiceLocator Adapter (see the list of those here as well)

First, setting up CastleWindsor and the common service locator. This code will be called from Application_Start in your Global.asax page:

 
            IWindsorContainer container = new WindsorContainer();
            container.AddComponent("authenticationProvider",
                                   typeof(IAuthenticationProvider), typeof(FormsAuthenticationProvider));
            container.AddComponent("userRepository",
                                   typeof(IUserRepository), typeof(UserRepository));
            container.AddComponent("membershipProvider",
                                   typeof(IMembershipProvider), typeof(MembershipProvider));
 
            ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));

Easy enough, yes? Now you can see how the default constructors we set up for our controllers work

 
public class MembershipController: Controller
    {
        private readonly IAuthenticationProvider authenticationProvider;
        private readonly IMembershipProvider membershipProvider;
 
        public MembershipController(IAuthenticationProvider authenticationProvider, IMembershipProvider membershipProvider)
        {
            this.authenticationProvider = authenticationProvider;
            this.membershipProvider = membershipProvider;
        }
 
        public MembershipController():this(ServiceLocator.Current.GetInstance<IAuthenticationProvider>(),
                                            ServiceLocator.Current.GetInstance<IMembershipProvider>()) {}
 
<...>

When the controller is instantiated by a test method, we can pass in a mocked IAuthenticationProvider and IMembershipProvider When it is instantiated using the default, parameterless constructor, it gets its implementations of IAuthenticationProvider and IMembershipProvider from the concrete implementations registered with our IoC contaner (Windsor) through the CommonServiceLocator. Viola!

Oh, but we’re not done. Here’s something about Asp.Net Mvc that I think is just exceptionally cool. Just by adding this one line of code:

            ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));

…we can now remove all those default parameterless constructors from our Controllers, and all the calls to the ComonServiceLocator, and let the ControllerFactory do it for us. We can still pass in mock objects when testing and the ControllerFactory will use the WindsorControllerFactory from the MvcContrib project to locate the implementations of the objects required by the controller’s constructor. Groovy, huh?

So, all together, here’s the code from Application_Start

protected void Application_Start() 
{
            IWindsorContainer container = new WindsorContainer();
 
            ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
 
            container.AddComponent("authenticationProvider",
                                   typeof(IAuthenticationProvider), typeof(FormsAuthenticationProvider));
            container.AddComponent("userRepository",
                                   typeof(IUserRepository), typeof(UserRepository));
            container.AddComponent("membershipProvider",
                                   typeof(IMembershipProvider), typeof(MembershipProvider));
 
            ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));
 
<...>
}

So, there you have it. And switching out CastleWindsor for another IoC container (that has adapters for ServiceLocator and ControllerFactory) is easy.

Reader's Comments

  1. JL |

    I commend you on doing this! This has been a great series of posts. I am also using this within Sh#arp Architecture. Within which assembly did you place your UserRepository and its Interface? I placed them in the Web.Controllers assembly. I am getting this error when logging in.

    Can’t create component ‘userRepository’ as it has dependencies to be satisfied.
    userRepository is waiting for the following dependencies:

    Services:
    - ProjectName.Core.User which was not registered.

    Here are the details for my ComponentRegistrar class:

    public static void AddComponentsTo(IWindsorContainer container)
    {
    AddGenericRepositoriesTo(container);
    AddCustomRepositoriesTo(container);
    container.AddComponent(”validator”,
    typeof(IValidator), typeof(Validator));
    container.AddComponent(”authenticationProvider”,
    typeof(IAuthenticationProvider), typeof(FormsAuthenticationProvider));
    container.AddComponent(”userRepository”,
    typeof(IUserRepository), typeof(UserRepository));
    container.AddComponent(”membershipProvider”,
    typeof(IMembershipProvider), typeof(MembershipProvider));

    }

    private static void AddCustomRepositoriesTo(IWindsorContainer container)
    {
    container.Register(
    AllTypes.Pick()
    .FromAssemblyNamed(”van.Data”)
    .WithService.FirstNonGenericCoreInterface(”van.Core.DataInterfaces”));
    }

    My User class resides inside Core. I have added the IUserRepository inside of the Core assembly. User Repository is inside of Data Assembly.

  2. JL |

    Revision to above. Initially I have placed the UserRepository and its Interface within the Web.Controllers assembly. I have subsequently moved them to Data and Core. This has had no bearing on the error.

  3. indomitablehef |

    Do you have a constructor for your UserRepository that takes a User object? That’s what the error message from Windsor appears to be saying. When it attempts to register the userRepository here:

    container.AddComponent(”userRepository”, typeof(IUserRepository), typeof(UserRepository));

    if there is a constructor for UserRepostiory like this:

    public UserRepository(User currentUser){}

    the registration of UserRepository would fail because the “User” type is not registered with Windsor.

  4. indomitablehef |

    And to follow up, if that is the case, I would suggest you reconsider that constructor, rather than registering ProjectName.Core.User with Windsor.

  5. JL |

    Can you post your UserRepository. I have fixed the error by removing the constructor as you say.

  6. indomitablehef |

    sure. I’m using S#arp Architecture, too, so my UserRepository only needs one additional method.

     public interface IUserRepository : IRepository<User>
        {
            User GetByUserName(string userName);
        }
     
     
     public class UserRepository : Repository<User>,IUserRepository
        {
    	#region Implementation of IUserRepository
     
    	public User GetByUserName(string userName)
    	{
                var criteria = Session.CreateCriteria(typeof(User))
                    .Add(Restrictions.Eq("Username", userName));
     
                return criteria.UniqueResult<User>();
    	}
            #endregion
        }
  7. Colin |

    Any chance you could put the code somewhere? I’m new to using Windsor and would like to see all the pieces together.
    thanks,
    Colin

  8. indomitablehef |

    I would have to extract it from some larger projects. There’s an implementation that includes most of it (for regular ASP.Net, not MVC, but it’s almost all the same), in the SokMunkae project on google code: http://code.google.com/p/sokmunkae/

Leave a Reply