ASP.NET MVC 3 RC ArearEgistration.Registerallareas () e conjuntos carregados dinamicamente
-
27-09-2019 - |
Pergunta
Atualmente, estou experimentando áreas carregadas dinamicamente com o ASP.NET MVC 3 RC. Eu já vi isso escrito em muitos lugares que não é para isso que as áreas se destinam e (pelo menos pré-MVC 2) não é possível, digamos aqui por exemplo.
Mas ainda! Deveria ser possível fazê -lo funcionar, certo? Criei uma solução, adicionei um projeto MVC 3, adicionei uma área e algum conteúdo. Tudo está funcionando bem. Agora, criei um novo projeto de biblioteca de classes (na mesma solução), adicionei uma referência a ele do projeto MVC e comecei a mover-se sobre as peças relacionadas à área para a biblioteca. Alterou o diretório de saída do projeto da biblioteca para o dobrador da área do projeto MVC e garantiu que as visualizações e seu web.config sejam copiadas para o dobrador de saída.
Depois de ler muito sobre como você não poderia ter áreas externas, foi um pouco surpreendente que isso funcionasse. Não há problema realmente! O problema começa quando removo a referência entre os projetos e, em vez disso, carrego a biblioteca no código. (Antes de ligar AreaRegistration.RegisterAllAreas()
.) Agora não funciona. De forma alguma.
Eu tenho cutucado um pouco na fonte do MVC 3, e o problema parece estar com BuildManager.GetReferencedAssemblies()
que é usado para fazer com que as assembléias procurem implementações de AreaRegistration
.
Agora, não tenho 100% de certeza sobre isso, mas parece que esse método apenas analisa os conjuntos que estavam presentes/referenciados em tempo de compilação, alguém pode confirmar se isso é de fato?
Eu depurei isso, e esse método-chamada não encontra a montagem que carreguei pouco antes da chamada. Pode ser por causa de outra coisa que eu perdi, talvez ... alguma idéia?
Solução
A maneira como as coisas funcionam é um pouco complicada.
GetReferencedAssemblies
Inclui conjuntos referenciados, não carregados conjuntos. Isso inclui:
- todos os assemblies mencionados no web.config do seu aplicativo (como
System.Web.Mvc
) - tudo herdado do root web.config, que inclui coisas como
System
,System.Web
E outros que você não precisa se adicionar. (Você pode dar uma olhada na lista aqui:C:\Windows\Microsoft.Net\Framework\v4.0.30319\web.config
).
Ele também contém um especial*
item, qual: - Inclui tudo no seu site
bin
pasta
Então, agora pegue seu aplicativo v1 (tudo em um único aplicativo). Tudo funciona porque o código do aplicativo é compilado na pasta BIN, que é incluída automaticamente. Além disso, todas as vistas da área etc. estão no próprio aplicativo para que sejam acessíveis.
Agora, no App V2 (projeto diferente com uma referência Proj-to-Proj e uma tarefa de construção personalizada que copia as visualizações para o local certo em seu aplicativo principal) tudo ainda funciona, porque, por padrão, uma referência proj-to-proj significa que o binário da biblioteca de classes é copiado para a pasta BIN do seu aplicativo. Portanto, pelas regras acima, o código de área ainda é carregado corretamente. O fato de você definir o caminho de saída da biblioteca como algum local na pasta de áreas do aplicativo principal não faz diferença - você acaba com duas cópias do binário.
Agora, no App v3 (sem proj-proj ref, montagem da biblioteca de área carregada manualmente) seu conjunto de biblioteca é carregado muito tarde. Quando seu código executar o conjunto de montagens referenciadas já foi bloqueado e não pode mais ser alterado.
Existe uma maneira de executar o código e adicionar itens à lista de assemblies registrados: você pode fazê -lo usando o AddReferencedAssembly
método que devo ser invocado de um PreApplicationStartMethodAttribute
método.
É claro que você ainda precisa lidar com a forma como gerencia seus arquivos de visualização. A maneira como você o configura atualmente é praticamente a mesma que ter as visualizações no aplicativo principal (uma vez que elas efetivamente são copiadas para o local certo).
Outras dicas
1 - Separe suas áreas MVC em diferentes projetos de MVC a serem compilados em suas próprias assembléias separadas
2 - Adicione isso à sua classe AssemblyInfo.cs, para chamar um método quando o aplicativo for carregado
[assembly: PreApplicationStartMethod(typeof(PluginAreaBootstrapper), "Init")]
3 - Aqui está como é o método init quando é invocado durante a carga
public class PluginAreaBootstrapper
{
public static readonly List<Assembly> PluginAssemblies = new List<Assembly>();
public static List<string> PluginNames()
{
return PluginAssemblies.Select(
pluginAssembly => pluginAssembly.GetName().Name)
.ToList();
}
public static void Init()
{
var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas");
foreach (var file in Directory.EnumerateFiles(fullPluginPath, "*Plugin*.dll"))
PluginAssemblies.Add(Assembly.LoadFile(file));
PluginAssemblies.ForEach(BuildManager.AddReferencedAssembly);
}
}
4 - Adicione um RazorviewEngine personalizado
public class PluginRazorViewEngine : RazorViewEngine
{
public PluginRazorViewEngine()
{
AreaMasterLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
};
AreaPartialViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
};
var areaViewAndPartialViewLocationFormats = new List<string>
{
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
};
var partialViewLocationFormats = new List<string>
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
var masterLocationFormats = new List<string>
{
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
};
foreach (var plugin in PluginAreaBootstrapper.PluginNames())
{
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.cshtml");
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml");
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{1}/{0}.cshtml");
masterLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{1}/{0}.vbhtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.cshtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{0}.cshtml");
partialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/Shared/{0}.vbhtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.cshtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/{1}/{0}.cshtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/{1}/{0}.vbhtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/Shared/{0}.cshtml");
areaViewAndPartialViewLocationFormats.Add(
"~/Areas/" + plugin + "/Areas/{2}/Views/Shared/{0}.vbhtml");
}
ViewLocationFormats = partialViewLocationFormats.ToArray();
MasterLocationFormats = masterLocationFormats.ToArray();
PartialViewLocationFormats = partialViewLocationFormats.ToArray();
AreaPartialViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray();
AreaViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray();
}
}
5 - Registre suas áreas de seus diferentes projetos de MVC (área)
namespace MvcApplication8.Web.MyPlugin1
{
public class MyPlugin1AreaRegistration : AreaRegistration
{
public override string AreaName
{
get { return "MyPlugin1"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"MyPlugin1_default",
"MyPlugin1/{controller}/{action}/{id}",
new {action = "Index", id = UrlParameter.Optional}
);
}
}
}
O SourCecode e as referências adicionais podem ser encontradas aqui:http://blog.longle.io/2012/03/29/building-a-compofase-mvc3-application-with-pluggable- areas