MEF и MVC 3 - как динамически загружать встроенные представления из контейнера mef?
Я создаю приложение MVC 3, где используется MEF. Основная идея заключается в том, чтобы иметь подключаемый механизм, в котором модели, контроллеры и представления загружаются динамически во время выполнения из контейнера mef.
Каждый плагин / модуль состоит из двух сборок:
Module1.Data.dll (содержит определения моделей)Module1.Web.dll (содержит контроллеры и представления)и помещаются в каталог плагинов внутри bin веб-приложения:
WebApp / Bin / Плагины / Module1.Data.dllWebApp / Bin / Плагины / Module1.Web.dllWebApp / Bin / Плагины / Module2.Data.dllWebApp / Bin / Плагины / Module2.Web.dllWebApp / Bin / Плагины / ModuleCore.Data.dllWebApp / Bin / Плагины / ModuleCore.Web.dllи т.д...Существует также основной модуль, на который ссылаются все остальные модули: ModuleCore.Data.dll и соответственно ModuleCore.Web.dll.
Затем в Global.asax контейнер строится следующим образом:
AggregateCatalog catalog = new AggregateCatalog();
var binCatalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "Module*.dll");
var pluginsCatalot = new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"), "Module*.dll");
catalog.Catalogs.Add(binCatalog);
catalog.Catalogs.Add(pluginsCatalot);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
AppDomain.CurrentDomain.AppendPrivatePath(Path.Combine(HttpRuntime.BinDirectory, "Plugins"));
CustomViewEngine создан и зарегистрирован и используется дляпоиск взглядов в сборке модуля:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
фабрика контроллеров для загрузки контроллеров из контейнера:
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container));
а также пользовательский поставщик виртуальных путей для получения сборок из контейнера:
HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider());
Итак, вся инфраструктура для работы с подключаемыми моделями, контроллерами и представлениями готова. Теперь все работает ... кроме одного -строго типизированные взгляды.
Чтобы проиллюстрировать проблему более подробно, давайте подготовим сцену:
Модель UserDTO находится в Module1.Data.dllShowUserController.cs находится в Module1.Web.dll / Контроллеры /Index.cshtml находится в Module1.Web.dll / Views / ShowUser (с объявленным @model Module1.Data.UserDto)Теперь мы делаем следующее:
Запустите приложение и перейдите в HOST / ShowUser / Index (метод действия Index выполняется на ShowUserController и извлекается представление Index.cshtml)После того, как представление Index.cshtml получено - начинается компиляция (от RazorBuildProvider)Возникают исключения: «не удается найти тип данных в пространстве имен Module1», другими словами, UserDTO не может быть найден при динамическом построении представленияТаким образом, кажется, что компилятор / строитель не просматривал папку bin / Plugins для Module1.Data.dll, потому что, когда я копировал этот файл в папку bin, он был в порядке.
Вопрос / проблема: почему конструктор не заглянул в папку bin / Plugins, хотя этот каталог был добавлен методом AppDomain.CurrentDomain.AppendPrivatePath? Как добавить частные пути для сборщика сборок, чтобы папка плагинов была учтена ??
Мне удалось немного поработать, создав CustomRazorBuildProvider, который переопределяет стандартный:
public class CustomRazorBuildProvider : RazorBuildProvider
{
public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder)
{
Assembly a = Assembly.LoadFrom(Path.Combine(HttpRuntime.BinDirectory, "Plugins", "Module1.Data.dll"));
assemblyBuilder.AddAssemblyReference(a);
base.GenerateCode(assemblyBuilder);
}
}
но недостаток этого решения заключается в том, что каждый раз, когда представление компилируется, необходимо добавлять ссылки на все сборки в папке плагинов, что может вызвать проблемы с производительностью позже, когда будет использоваться большое количество плагинов.
Какие-нибудь более хорошие решения?