Я пытаюсь создать среду, позволяющую динамически импортировать контроллеры и представления в приложение MVC.Вот как это работает:
- Я использую .NET 4, ASP.NET MVC 3 RC и Razor ViewEngine
- Контроллеры экспортируются и импортируются с использованием MEF для проекта -Я называю набор контроллеров и представлений из данного проекта «Модулем»
- Сборки, обнаруженные с использованием MEF, динамически ссылаются на BuildManager с использованием метода запуска перед приложением и
BuildManager.AddReferencedAssembly
. - Двоичные файлы (из экспорта проекта) и представления копируются в структуру папок целевого проекта с использованием события построения
- Контроллеры выбираются с использованием настраиваемой фабрики контроллеров, которая наследуется от DefaultControllerFactory и переопределяет GetControllerType ()
- Viewsвыбираются с помощью пользовательского механизма представления, который наследует от RazorViewEngine и переопределяет GetView () и GetPartialView (), чтобы он мог искать представления в директориях представления для конкретного модуля
Пока все работает , кроме для представлений, использующих строго типизированную модель.Представления, использующие динамическую модель, работают нормально, но когда я указываю тип модели с помощью @model
, я получаю YSOD, который говорит: «Представление« Индекс »или его мастер не найден».
При отладке моегоВ реализации ViewEngine я вижу, что: this.VirtualPathProvider.FileExists(String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller")))
возвращает значение true, в то время как
this.FileExists(controllerContext, String.Format(this.ViewLocationFormats[2], viewName, controllerContext.RouteData.GetRequiredString("controller")))
возвращает значение false.
Если смотреть в Reflector, реализация RazorViewEngine для FileExists()
в конечном итоге завершается следующим образом:
return (BuildManager.GetObjectFactory(virtualPath, false) != null);
Однако я не могу просмотреть BuildManager.GetObjectFactory()
из Reflector, потому что он каким-то образом скрыт.
Я подозреваю, что это как-то связано с тем, что тип моделитип, который загружается из MEF, но поскольку я уже ссылаюсь на сборки, обнаруженные MEF из BuildManager, у меня нет потенциальных клиентов.Может ли кто-нибудь предоставить немного больше информации о том, что может происходить?
Обновление: Оказывается, я использовал устаревшую версию Reflector по сравнению с .NET 4. Я вижу,GetObjectFactory () сейчас, но я не могу найти ничего полезного.Я попытался добавить это в мою перегрузку FindView ():
try {var path = String.Format (this.ViewLocationFormats [2], viewName, controllerContext.RouteData.GetRequiredString ("controller"));var objFactory = System.Web.Compilation.BuildManager.GetObjectFactory (virtualPath: path, throwIfNotFound: true);} catch {}
К сожалению, objFactory
обнуляется, и исключение не выдается.Все биты, которые имеют дело с ошибками компиляции, являются частными методами или типами, поэтому я не могу отладить ничего из этого, но даже кажется, что они в конечном итоге выдают исключение, которое, похоже, не происходит.Похоже, я снова в тупике.Справка!
Обновление 2
Я обнаружил, что в момент вызова FindView (), если я вызываю AppDomain.CurrentDomain.GetAssemblies()
, сборкатип модели в включен .Однако я не могу загрузить тип, используя Type.GetType()
.
Обновление 3
Вот что я вижу: ![not found](https://i.stack.imgur.com/zV3ot.png)
Обновление 4
Вот реализация ViewEngine:
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Hosting;
using System.Web.Compilation;
namespace Site.Admin.Portal
{
public class ModuleViewEngine : RazorViewEngine
{
private static readonly String[] viewLocationFormats = new String[]
{
"~/Views/{0}/{{1}}/{{0}}.aspx",
"~/Views/{0}/{{1}}/{{0}}.ascx",
"~/Views/{0}/{{1}}/{{0}}.cshtml",
"~/Views/{0}/Shared/{{0}}.aspx",
"~/Views/{0}/Shared/{{0}}.ascx",
"~/Views/{0}/Shared/{{0}}.cshtml"
};
public ModuleViewEngine(IModule module)
{
this.Module = module;
var formats = viewLocationFormats.Select(f => String.Format(f, module.Name)).ToArray();
this.ViewLocationFormats = formats;
this.PartialViewLocationFormats = formats;
this.AreaViewLocationFormats = formats;
this.AreaPartialViewLocationFormats = formats;
this.AreaMasterLocationFormats = formats;
}
public IModule Module { get; private set; }
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache)
{
var moduleName = controllerContext.RouteData.GetRequiredString("module");
if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
{
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
else return new ViewEngineResult(new String[0]);
}
public override ViewEngineResult FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)
{
var moduleName = controllerContext.RouteData.GetRequiredString("module");
if (moduleName.Equals(this.Module.Name, StringComparison.InvariantCultureIgnoreCase))
{
var baseResult = base.FindView(controllerContext, viewName, masterName, useCache);
return baseResult;
}
else return new ViewEngineResult(new String[0]);
}
}
}