У меня есть области MVC во внешних библиотеках, которые имеют свой собственный регистрационный код области, как и в обычной области MVC. Эта регистрация области вызывается для каждого dll (модуля), и я убедился, что RouteTable содержит все маршруты из загруженных модулей после завершения загрузки.
Когда я ссылаюсь на эти внешние области на главном сайте, они попадают в каталог bin и прекрасно загружаются. То есть, когда делается запрос на маршрут, который существует во внешней библиотеке, правильный тип передается на мою фабрику пользовательских контроллеров (Ninject) и может быть создан экземпляр контроллера.
Однако, как только я переместил эти dll за пределы каталога bin (скажем, в папку Modules), возникает проблема с маршрутизацией. Я проверил, что RouteTable имеет все требуемые маршруты, но к тому времени, когда запрос попадает на фабрику контроллеров Ninject, запрашиваемый тип становится пустым. Из прочтения здесь ссылки SO здесь кажется, что это происходит, когда ASP.NET MVC не может найти контроллер, соответствующий запрошенному маршруту, или не знает, как понять маршрут.
При внешней загрузке модулей я убедился, что загружаемые модули загружаются в домен приложения с помощью вызова Assemby.LoadFrom(modulePath);
Я провел некоторое исследование, и кажется, что при попытке загрузить библиотеку вне bin вам нужно указать частное исследование в app.config, как указано ; . У меня установлено 'bin \ Modules', куда также перемещаются модули области mvc.
У кого-нибудь есть идеи, почему простое перемещение проекта области mvc за пределы папки bin может привести к тому, что запрошенный тип, переданный на фабрику контроллеров, станет нулевым, что приведет к созданию экземпляра контроллера?
Edit:
- Все маршруты, зарегистрированные во внешних областях, имеют пространство имен контроллера, указанное в маршруте
Ниже приведен фрагмент кода, который создает новое ядро Ninject, читает список имен модулей из включаемого файла и затем выполняет поиск включенных модулей в каталоге bin / Modules. Модуль загружается через загрузчик сборок, регистрирует свои области и затем загружается в ядро ninject.
// comma separated list of modules to enable
string moduleCsv = ConfigurationManager.AppSettings["Modules.Enabled"];
if (!string.IsNullOrEmpty(moduleCsv)) {
string[] enabledModuleList = moduleCsv.Replace(" ", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
_Modules = enabledModuleList ?? new string[0];
// load enabled modules from bin/Modules.
var moduleList = Directory.GetFiles(Server.MapPath("~" + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "Modules"), "*.dll");
foreach (string enabledModule in enabledModuleList) {
string modulePath = moduleList.Single(m => m.Contains(enabledModule));
// using code adapted from from AssemblyLoader
var asm = AssemblyLoader.LoadAssembly(modulePath);
// register routes for module
AreaRegistrationUtil.RegisterAreasForAssemblies(asm);
// load into Ninject kernel
kernel.Load(asm);
}
}
Это суть фабрики контроллеров Ninject, которая получает вышеупомянутое ядро Ninject и обрабатывает запросы на создание контроллеров. Для контроллеров, которые существуют в сборке в bin / Modules, GetControllerType (...) возвращает нулевое значение для запрошенного имени контроллера.
public class NinjectControllerFactory : DefaultControllerFactory
{
#region Instance Variables
private IKernel _Kernel;
#endregion
#region Constructors
public NinjectControllerFactory(IKernel kernel)
{
_Kernel = kernel;
}
protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
{
// Is null for controller names requested outside of bin directory.
var type = base.GetControllerType(requestContext, controllerName);
return type;
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
IController controller = null;
if (controllerType != null)
controller = _Kernel.Get(controllerType) as IController;
return controller;
}
}
Обновление Ninject Nuget Install
По какой-то причине мне не удалось установить Ninject.MVC3 через NuGet. Visual Studio выдавала ошибку schemaVersion при нажатии кнопки установки (я установил другие пакеты Nuget, например, ELMAH).
Я обнаружил кое-что еще интересное, и это то, что если я передам дополнительные сборки модулей в NinjectControllerFactory, у меня есть и выполнит поиск, когда тип не может быть разрешен, он найдет правильный тип и сможет построить контроллер. Это приводит к еще одной странной проблеме.
Первый маршрут, запрашиваемый у внешнего модуля, - это / Account / LogOn в модуле авторизации и регистрации. Поставщик виртуального пути выдает здесь ошибку после того, как он обнаружил представление, и пытается выдать его с жалобой на отсутствующее пространство имен. Это приводит к сбою маршрута ошибки, который обрабатывается модулем ErrorHandling. Как ни странно, это загружается и рендерит нормально!
Так что я все еще застрял с двумя проблемами;
1) Необходимость немного хитроумного взлома и передачи дополнительных сборок модулей в NinjectControllerFactory, чтобы иметь возможность разрешать типы для контроллеров во внешних модулях.
2) Ошибка с одним конкретным модулем, когда он жалуется на то, что пространство имен не найдено
Эти две проблемы, очевидно, связаны, потому что загрузка сборки просто не загружается и делает все доступное, что должно быть. Если все эти области MVC загружены из каталога bin, все работает нормально. Так что это явно проблема с пространством имен / загрузкой сборки.