2013-02-15 1 views
12

У меня возникла проблема с разрешением 404 ответов в моем проекте Asp.Net MVC 4. Он построен в VS2012, ориентированном на 4.5.404 на контроллерах во внешних сборках

У меня есть предварительно скомпилированные представления и контроллеры, встроенные в автономные библиотеки DLL. Я могу динамически загружать библиотеки DLL и проверять их из моего основного проекта, даже вызывать методы на них; однако, похоже, что MVC Framework не знает контроллеров. Я рядом, но чего-то не хватает.

Справочная информация о контроллерах и представлений

контроллеры встроены в отдельный проект MVC и наследовать от Controller. Ничего интересного не происходит. Представления используют RazorGenerator и становятся классами, которые живут в проекте.

Результатом проекта является DLL, которая правильно содержит контроллеры и представления.

DLL реализует определенный интерфейс, мы будем называть его IPlugin в отдельном классе (не являющемся частью контроллера) в библиотеке.

Загрузка библиотеки DLL

Запуск от имени администратора в Visual Studio я компилирую мое приложение, которое организовано под IIS. С построенным проектом я бросаю DLL плагина в каталог «Плагины». Без отладки (это становится важным позже) я открываю IE и перехожу на сайт. Обратите внимание, что на этом этапе приложение было создано, но никогда не запускается, поэтому запускаются события запуска. Все, что я могу сделать, все равно остается неизменным, если я повторно использую пул приложений.

У меня есть Startup класса с двумя методами, PreStart и PostStart и вызывать методы с использованием WebActivator.PreApplicationStartMethod и WebActivator.PostApplicationStartMethod соответственно.

PreStart где я сделать следующее:

  • Получить список всех плагинов DLL, в моем каталоге «Plugins»
  • Скопируйте все плагины AppDomain.CurrentDomain.DynamicDirectory
  • загрузить тип ... если она содержит IPlugin я тогда
    • Добавить сборку в BuildManager
    • Зов некоторые из методов в классе, что IMP lements IPlugin

В 'PostStart' Я делаю это немного кода (на основе кода из RazorGenerator.Mvc):

foreach (var assembly in Modules.Select(m=>m.Value)) 
{ 
    var engine = new PrecompiledMvcEngine(assembly) 
    { 
     UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal 
    }; 

    ViewEngines.Engines.Insert(0, engine); 
    VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
} 

Modules в этом контексте является пара ключ/значение, где значения - это загруженные сборки. Цель этого кода - убедиться, что MVC осведомлен о представлениях, добавив механизм просмотра для каждой сборки, который знает, как разрешить представления (это часть RazorGenerator).

Как я знаю, что я Close (но явно не достает сигару)

IPlugin определяет метод, называемый RegisterRoutes, где, как вы уже догадались, маршруты должны быть зарегистрированы для тех, кто реализует интерфейс. Я вызываю этот метод в PreStart и добавляются маршруты - я подтвердил, что они существуют в моей таблице маршрутов. Например, на маршруте, определенный в мой плагин, созданный с помощью динамического вызова метода во время PreStart, я вижу что-то вроде этого как DataToken при рассмотрении моих маршрутов:

Namespaces = Plugin.Name.Controllers 

Таким образом, маршрут зарегистрирован, сборка загружена, я проверил, что DLL правильно скопирована в DynamicDirectory AppDomain. Я могу вызвать участников классов, которые динамически загружаются во время выполнения. Но когда я перехожу к URL-адресу, который соответствует маршруту , я получаю 404. Это не a «не удалось найти местонахождение» YSOD, это больше похоже на то, что вы не обнаружили контроллер вообще.

Вот часть, которая смущает меня из-за меня: если в данный момент, ничего не делая, я возвращаюсь в Visual Studio и нажимаю F5 ... все работает.

Это похоже на то, что Visual Studio каким-то образом узнает о контроллере, который я не могу определить, и MVC Framework набирает его.

Наконец, Вопрос

Что мне не хватает, и как я могу получить MVC Framework, чтобы быть в курсе моего контроллера?

И, в этот момент, если вы все еще читаете это, спасибо. :)

+0

1. Выполняется ли VS с использованием Cassini? Попробуйте изменить его на IIS Express и проверьте, продолжает ли он работать правильно. 2. Попробуйте установить [RouteDebugger] (http://nuget.org/packages/routedebugger) - возможно, это может дать вам некоторые подсказки относительно правильности регистрации маршрутов в IIS – Pranav

+0

Спасибо @Pranav, но он уже на IIS. Отладчик маршрута показывает, что маршруты работают. – MisterJames

+1

Может ли это быть проблемой? http://stackoverflow.com/questions/14971895/using-precompiledmvcengine-findview-throws-invalidoperationexception-and-looks-f – Tengiz

ответ

5

Оказывается, что это ошибка в самой Asp.Net.

После обсуждения вопроса с Эйлоном Липтоном из Асп.Чистая команда и думая, что это было чем-то нехорошо в MVC Framework, Эйлон и несколько членов команды врылись в вещи и обнаружили, что ошибка была на более низком уровне в этом разговоре: http://aspnetwebstack.codeplex.com/discussions/403529

Они также предложили обходное решение, которое включало другое звоните по BuildManager после вызова AddReferencedAssembly, который я реализовал с помощью следующего кода:

// Add the plugin as a reference to the application 
    BuildManager.AddReferencedAssembly(assembly); 
    BuildManager.AddCompilationDependency(assembly.FullName); 

Это позволяет добавить дополнительные контроллеры/Сост просмотры при запуске в вашей предварительной заявке стадии инициализации. То, что я сейчас делаю, - это перебирать список DLL в моем каталоге плагинов и нажимать их на BuildManager, как указано выше.

Единственное ограничение заключается в том, что вы не можете удалить сборок или очистить кеш динамически. Единственный способ, который я нашел для этого, - добавить ранее неизвестную сборку к ссылочным сборкам и зависимостям компиляции. Я экспериментирую с динамическим испусканием новой сборки во время инициализации перед приложением, чтобы я мог, эффективно, очистить кеш и удалить ранее включенные плагины, подделав новую сборку.

Надеюсь, что это поможет кому-то еще.

Cheers.

+0

Большое спасибо за подробный вопрос и ответ. Одна вещь, которую я не понимаю, - это почему вы не используете фабрику пользовательских контроллеров (см. 'ControllerBuilder.Current.SetControllerFactory')? Это позволит вам иметь полный контроль над тем, когда выдать какой контроллер, не так ли? – Dejan

1

Похоже, что этот вопрос:

MVC использует сборочный-квалифицированное имя типа вида двигателя к устранения неоднозначности записей кэша вид с другой точки зрения двигателей. Так что у не возможно иметь более одного объекта PrecompiledMvcEngine (как , когда у вас есть предварительно скомпилированные представления в более чем одной сборке). Проблема может быть решена путем создания другого производного класса от PrecompiledMvcEngine для каждой сборки. Или путем создания отдельного производного класса , параметризованного некоторым типом сборки.

Артикул here.

+0

Спасибо, Nenad, но в это время я запускаю только один плагин, поэтому есть только один, который пытается загрузить. Это может иметь значение, но у меня также было сразу два плагина, поэтому я не уверен, что это произойдет. Кроме того, это не касается проблемы, с которой я столкнулся, поскольку контроллер не находится. Это ключ к щедрости. Приветствия. – MisterJames

+0

Можете ли вы дать более подробную информацию об ошибке? трассировки стека? – Nenad

+0

Хотелось бы. Все, что я получаю, - это 404 на контроллере, когда я пытаюсь получить доступ к представлению. У меня есть Log4Net и ELMAH, и нет ничего плохого в том, что я собираю (с протоколированием на каждом шагу). У меня есть более подробные сведения, которые я добавлю к вопросу позже, поговорив с некоторыми членами команды Asp.Net на этой неделе. – MisterJames

0

@MisterJames, посмотрите на это:

Asp.Net Mvc Pluggable Application

Я надеюсь, что это полезно.

+0

Felipe, это использование областей в проекте из одного и того же решения и совершенно другой подход. Мои потребности - иметь автономные решения для плагинов. Я объясню это более подробно, чтобы узнать, есть ли какие-либо подсказки, но, поскольку это стоит, я не верю, что это поможет в моем сценарии. – MisterJames