more windsor mvc controller factory

This is the fifth in a series of posts illustrating building a custom Windsor registration process. In my last post I reached a point where my code satisfied all my current specifications for a Windsor controller factory for ASP.NET MVC.

I now want to add a requirement where the type is resolved from the IOC container using the full type name in lower case - this will then match my previous controller registration process.
I therefore change both sets of specifications to cope with this - the first one is changed to expect the call to the Resolve method to use the lower case FullName:

[Observations]
public class when_a_controller_is_requested_from_the_windsor_controller_factory :
  observations_for_a_sut_with_a_contract<IControllerFactory, WindsorControllerFactory>
{
    private static IWindsorContainer container;
    private static RequestContext requestContext;
    private static string controllerName;
    private static IController returnedController;
    private static IController theExpectedController;
    private static string theExpectedControllerNameResolved;

    private context c = () =>
            {
                container = the_dependency<IWindsorContainer>();
                controllerName = "FakeController";
                theExpectedControllerNameResolved = typeof(FakeController).FullName.ToLower();
                theExpectedController = an<IController>();
                container.expect_at_least_once(w => w.Resolve<IController>(controllerName))
                                         .IgnoreArguments()
                                         .Return(theExpectedController);
            };

    private because b = () => returnedController = sut.CreateController(null, controllerName);

    [Observation]
    public void should_ask_the_container_to_resolve_the_controller()
    {
        container.was_told_to(c => c.Resolve<IController>(theExpectedControllerNameResolved));
    }

    [Observation]
    public void should_return_the_controller_obtained_from_the_container()
    {
        returnedController.should_be_equal_to(theExpectedController);
    }
}

This results in the tests failing due to an expectation violation:
Rhino.Mocks.Exceptions.ExpectationViolationException: IWindsorContainer.Resolve<System.Web.Mvc.IController>("apollo.infrastructure.windsor.fakecontroller"); Expected #1, Actual #0.

...and the second set of specifications are changed so that the registration is made with the lower case FullName:

[Observations]
public class simple_when_a_controller_is_requested_from_the_windsor_controller_factory :
  observations_for_a_sut_with_a_contract<IControllerFactory, WindsorControllerFactory>
{
    private static Type controllerType;
    private static IController returnedController;
    private static IWindsorContainer container;

    private context c = () =>
                        {
                            controllerType = typeof (FakeController);
                            container = new WindsorContainer();
                            container.AddComponentLifeStyle(controllerType.FullName.ToLower(),
                                              controllerType, LifestyleType.Transient);
                        };

    public override IControllerFactory create_sut()
    {
       return new WindsorControllerFactory(container);
    }

    private because b = () => returnedController = sut.CreateController(null,
                                                                      controllerType.Name);

    [Observation]
    public void should_return_an_instance_of_the_controller()
    {
        returnedController.should_not_be_null();
    }

    [Observation]
    public void should_return_the_correct_controller()
    {
        returnedController.should_be_an_instance_of<FakeController>();
    }
}

The tests fail with a ComponentNotFoundException because I am now registering with the full name, but resolving with the type name.
I now need to change the implementation code accordingly:

public class WindsorControllerFactory : IControllerFactory
{
    private IWindsorContainer container;

    public WindsorControllerFactory(IWindsorContainer container)
    {
        this.container = container;
    }

    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controllerType = GetControllerTypeFromLoadedAssemblies(controllerName);

        return container.Resolve<IController>(controllerType.FullName.ToLower());
    }

    private Type GetControllerTypeFromLoadedAssemblies(string controllerName)
    {
        Type controllerType = null;

        var assemblies = AppDomain.CurrentDomain.GetApolloAssemblies();
        foreach (var assembly in assemblies)
        {
            controllerType = assembly.GetTypes().Where(t => t.Name == controllerName)
                                                .FirstOrDefault();
            if (controllerType != null)
                return controllerType;
        }

        return null;
    }

    public void ReleaseController(IController controller)
    {
        throw new NotImplementedException();
    }
}

This new implementation looks in the relevant currently loaded assemblies and finds the type name - it uses a custom AppDomain extension method to find the assemblies:

public static class AppDomainExtensions
{
    public static IEnumerable<Assembly> GetApolloAssemblies(this AppDomain appDomain)
    {
        return AppDomain.CurrentDomain.GetAssemblies()
                        .Where(a => a.FullName.StartsWith("Apollo"));
    }
}

This forms the working implementation until I then discover that the ASP.NET MVC framework actually calls the CreateController method with the first part of the name of the controller - in this example it would call with "Fake" for the "FakeController".

In my next post I'll illustrate how I introduce new specifications to meet this new requirement.



0 comments: