MEF e MVC 3 - como carregar visualizações incorporadas dinamicamente do contêiner me
Estou construindo um aplicativo MVC 3 onde o MEF é usado. A idéia principal é ter um mecanismo de plug-in no qual modelos, controladores e visualizações são carregados dinamicamente durante o tempo de execução do contêiner me
ada plugin / módulo consiste em dois conjuntos:
Module1.Data.dll (contém definições de modelo Module1.Web.dll (contém controladores e visualizações)e são colocados no diretório Plugins na lixeira do aplicativo da web:
WebApp / Bin / Plugins / Module1.Data.dll WebApp / Bin / Plugins / Module1.Web.dll WebApp / Bin / Plugins / Module2.Data.dll WebApp / Bin / Plugins / Module2.Web.dll WebApp / Bin / Plugins / ModuleCore.Data.dll WebApp / Bin / Plugins / ModuleCore.Web.dlletc ...ambém existe um módulo principal referenciado por todos os outros módulos: ModuleCore.Data.dll e respectivamente ModuleCore.Web.dl
Então, no Global.asax, o contêiner é compilado da seguinte maneira:
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 é criado, registrado e usado parafinding views na montagem do módulo:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
controller factory para carregar controladores do contêiner:
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container));
e também provedor de caminho virtual personalizado para obter montagens do contêiner:
HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider());
Ok, então toda a infraestrutura para lidar com modelos, controladores e visualizações conectáveis está pronta. Agora tudo funciona ... exceto uma coisa - visualizações fortemente digitadas.
ara ilustrar o problema com mais detalhes, vamos preparar a cen
modelo @UserDTO está localizado em Module1.Data.dll ShowUserController.cs está localizado em Module1.Web.dll / Controllers / Index.cshtml está localizado em Module1.Web.dll / Views / ShowUser (com @model Module1.Data.UserDto)Agora fazemos o seguinte:
Execute o aplicativo e vá para HOST / ShowUser / Index (o método de ação Index é executado no ShowUserController e a visualização Index.cshtml é buscada)Depois que a visualização Index.cshtml é buscada - a compilação inicia (por RazorBuildProvider) As exceções são lançadas: "não é possível encontrar o tipo de dados no espaço para nome Module1"; em outras palavras, não foi possível encontrar o UserDTO durante a construção dinâmica da exibiçãoParece que o compilador / construtor não procurou na Module / Data.dll a pasta bin / Plugins, porque quando eu copiei esse arquivo na pasta bin - ele estava be
Pergunta / problema: por que o construtor não procurou na pasta bin / Plugins, embora esse diretório tenha sido adicionado pelo método AppDomain.CurrentDomain.AppendPrivatePath? Como adicionar caminhos particulares para o construtor de montagem uma vez, para que a pasta de plugins seja levada em consideração
Eu consegui resolver isso criando CustomRazorBuildProvider que substitui o padrão:
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);
}
}
mas a desvantagem desta solução é que sempre que a exibição é compilada, é necessário adicionar referências a todos os assemblies na pasta Plugins, o que pode causar problemas de desempenho posteriormente, quando muitos plug-ins serão usado
Qualquer solução melho