ASP.NET MVC и Unity 1.2 Контейнерный вопрос - PullRequest
10 голосов
/ 21 августа 2009

Я пытаюсь использовать контейнер Unity, чтобы упростить юнит-тестирование моих контроллеров. Мой контроллер использует конструктор, который принимает интерфейс к хранилищу. В файле global.asax я создаю экземпляр UnityContainerFactory и регистрирую его в инфраструктуре MVC, а затем регистрирую репозиторий и его реализацию. Я добавил атрибут [Dependency] в параметр репозитория CTOR контроллера. Кажется, что все это работает нормально, за исключением того, что иногда фабричный GetControllerInstance (Type controllerType) вызывается более одного раза и передается пустой аргумент в качестве controllerType.

Первый вызов фабрики всегда корректен, и в качестве аргумента в качестве аргумента передается контроллерный тип «ProductsController». Но иногда фабрика вызывается еще пару раз после отображения представления с нулевым значением для контроллера, и я не уверен, почему. Когда передается правильное значение типа контроллера, «стек вызовов» имеет смысл для меня, но когда передается ноль, я не уверен, почему или кто делает вызов. Есть идеи?

Код и стеки вызовов для примера показаны ниже.

Стек вызовов, когда он работает

Test.DLL! Test.UnityHelpers.UnityControllerFactory.GetControllerInstance (System.Type controllerType = {Name = "ProductsController" FullName = "Test.Controllers.ProductsController"}) Строка 23 C # Test.DLL! Test._Default.Page_Load (отправитель объекта = {ASP.default_aspx}, System.EventArgs e = {System.EventArgs}) Строка 18 + 0x1a байт C #

стек вызовов при передаче значения NULL в controllerType

Test.DLL! Test.UnityHelpers.UnityControllerFactory.GetControllerInstance (System.Type controllerType = null) Строка 27 C #

Сначала я создал 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
        }
    }
}

Затем я добавил следующий код в файл global.asax, чтобы создать экземпляр фабрики контейнеров

    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());
    }

Приложение имеет один контроллер

public class ProductsController : Controller
{
    public IProductsRepository productsRepository;

    public ProductsController([Dependency]IProductsRepository productsRepository)
    {
       this.productsRepository = productsRepository;
    }
}

1 Ответ

9 голосов
/ 21 августа 2009

Вероятно, это связано с тем, что какой-то тип файла не сопоставлен с контроллером в ваших маршрутах. (изображения, например). По моему опыту, это будет происходить чаще, когда вы отлаживаете локально с помощью Cassini, поскольку Cassini позволяет всем запросам маршрутизироваться через ASP.NET, а в IIS многие запросы обрабатываются IIS для вас. По этой же причине вы не видите свой код в стеке для этого запроса. Если вы отключите опцию «Просто мой код» в Visual Studio, иногда вы сможете лучше понять эти вещи.

Это не единственная причина, по которой это может случиться, но это часто встречается.

Надлежащим решением было бы позволить базовому методу обрабатывать запрос в этих ситуациях. Обычно это простой запрос к файлу, который не должен оказывать на вас никакого влияния.

Самое простое, что нужно сделать, это открыть его так:

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

Это должно сделать это.

Чтобы узнать, для чего предназначен запрос, вы можете проверить HttpContext.Current.Request, чтобы увидеть, какой файл отсутствует в вашем маршруте. Много раз вы не хотите контролировать это, но вам будет легче узнать, каков источник запроса.

...