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?

Foi útil?

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top