10

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;
    }
}
3
  • Are you 100% certain that this line isn't returning null: return container.Resolve(controllerType) as IController; It seems unlikely, but that cast could easily return null if the resulting type was not an IController or the Resolve call failed. Commented Aug 21, 2009 at 5:01
  • Hi Anderson, As you can see from the call stack, null is being passed in. I also stopped at the line using the debugger and it is null prior to the cast. This call is the only function on the stack at the time. Which I also do not understand. Test.DLL!Test.UnityHelpers.UnityControllerFactory.GetControllerInstance(System.Type controllerType = null) Line 27 C# Commented Aug 21, 2009 at 14:18
  • That's what I thought I was seeing in your stack, but I wanted to check my assumptions to make sure. Commented Aug 21, 2009 at 16:04

1 Answer 1

9

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(requestContext, 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.

Sign up to request clarification or add additional context in comments.

4 Comments

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.
Question, why should we call [code]base.GetControllerInstance[/code] even though controllerType is null? Would it be better to just return null? I am getting the "The controller for path '' was not found or does not implement IController." message. Returning null fixes the issue.
@pmaroun: you can do either. The base one actually returns null. I recommended this at the time I wrote it (2 years ago) because I couldn't find any documentation on the expected return values, so I used the behavioral covenant established in the base Controller. The error you are seeing would be solved by either solution (because they both result in null).
Yep.. I just did some testing and found your comment to be true. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.