views:

814

answers:

1

I am trying to use the Unity container to make it easier to unit test my controllers. My controller uses a constructor that accepts an interface to a Repository. In the global.asax file, I instantiate a UnityContainerFactory and register it with the MVC framework and then register the repository and its implementation. I added the [Dependency] attribute to the controller’s CTOR Repository parameter. This all seems to work OK, except that occasionally the factory’s GetControllerInstance(Type controllerType) is called more than once and is passed a null argument as the controllerType.

The first call to the factory is aways correct and the controllerType “ProductsController” is passed-in as an argument. But sometimes, the factory is called a couple more times after the view has been displayed with a null value for the controller and I am not sure why. When the correct value of the controller type is passed that “Call Stack” makes sense to me, but when a null is passed, I am not sure why or who is making the call. Any ideas?

The code and call stacks for the example are shown below.

Call Stack when is works

Test.DLL!Test.UnityHelpers.UnityControllerFactory.GetControllerInstance(System.Type controllerType = {Name = "ProductsController" FullName = "Test.Controllers.ProductsController"}) Line 23 C# Test.DLL!Test._Default.Page_Load(object sender = {ASP.default_aspx}, System.EventArgs e = {System.EventArgs}) Line 18 + 0x1a bytes C#

Call Stack when NULL is passed at the controllerType

Test.DLL!Test.UnityHelpers.UnityControllerFactory.GetControllerInstance(System.Type controllerType = null) Line 27 C#

First I created a UnityControllerFactory

public class UnityControllerFactory : DefaultControllerFactory
{
    UnityContainer container;

    public UnityControllerFactory(UnityContainer container)
    {
        this.container = container;
    }

    protected override IController GetControllerInstance(Type controllerType)
    {
        if (controllerType != null)
        {
            return container.Resolve(controllerType) as IController;
        }
        else
        {
            return null; // I never expect to get here, but I do sometimes, the callstack does not show the caller
        }
    }
}

Next, I added the following code the global.asax file to instantiate the container factory

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);

        // Create Unity Container if needed
        if (_container == null)
        {
            _container = new UnityContainer();
        }

        // Instantiate a new factory
        IControllerFactory unityControllerFactory = new UnityControllerFactory(_container);

        // Register it with the MVC framework
        ControllerBuilder.Current.SetControllerFactory(unityControllerFactory);

        // Register the SqlProductRepository
        _container.RegisterType<IProductsRepository, SqlProductRepository>
            (new ContainerControlledLifetimeManager());
    }

The app has one controller

public class ProductsController : Controller
{
    public IProductsRepository productsRepository;

    public ProductsController([Dependency]IProductsRepository productsRepository)
    {
       this.productsRepository = productsRepository;
    }
}
+7  A: 

This is likely due to some file type not mapping to a controller in your routes. (images, for example). This will happen more often when you are debugging locally with Cassini in my experience since Cassini allows all requests to route through ASP.NET while in IIS a lot of requests are handled by IIS for you. This would also be why you don't see your code in the stack for this request. If you turn off the "Just My Code" option in Visual Studio, you can sometimes get a better hint about these things.

This is not the only reason this can happen, though, but it's common.

The appropriate thing to do would be to allow the base method handle the request in these situations. It's usually just a simple file request and shouldn't have any impact on you.

Simplest thing to do would be to gate it like this:

    if (controllerType != null)
    {
        return container.Resolve(controllerType) as IController;
    }
    else
    {
        return base.GetControllerInstance(controllerType);
    }

That ought to do it.

To see what the request is for, you might be able to check HttpContext.Current.Request to see what file is not in your route. A lot of times it's not something you care to control, but it'll make you feel better to know what the origin of the request is.

Anderson Imes
Thanks For the answer and the tips.....
Rick
I took your suggestion and looked at the HttpContext.Current.Request and noticed is was looking for favicon.ico. That explains why it worked sometimes and not others. When an exisiting instance of the browser was open, it didn't try to find favicon.ico.
Rick
Hey, good to know and glad I could help.
Anderson Imes