2010-11-22 23 views
13

Actualmente estoy experimentando con áreas cargadas dinámicamente con ASP.NET MVC 3 RC. Lo he visto escrito en muchos lugares que esto no es para lo que están destinadas las áreas, y (al menos antes de MVC 2) no es posible, digamos here por ejemplo.ASP.NET MVC 3 RC AreaRegistration.RegisterAllAreas() y ensamblados cargados dinámicamente

¡Pero aún así! Debería ser posible hacer que funcione, ¿verdad? Creé una solución, agregué un proyecto MVC 3, agregué un área y algo de contenido. Todo está funcionando bien. Ahora, creé un nuevo proyecto de biblioteca de clases (en la misma solución), agregué una referencia al proyecto MVC y comencé a trasladar las partes relacionadas con el área a la biblioteca. Se cambió el directorio de salida del proyecto de biblioteca a la carpeta de área del proyecto MVC y se aseguró de que las Vistas y su web.config se copiaran en la carpeta de salida.

Después de leer tanto acerca de cómo no se podían tener áreas externas, fue un poco sorprendente que esto funcionara. ¡Ningún problema realmente! El problema comienza cuando elimino la referencia entre los proyectos y, en su lugar, cargo la biblioteca en el código. (Antes de llamar al AreaRegistration.RegisterAllAreas().) Ahora no funciona. En absoluto.

He estado hurgando un poco en la fuente de MVC 3, y el problema parece ser con BuildManager.GetReferencedAssemblies() que se usa para que los ensambles busquen implementaciones de AreaRegistration.

Ahora, no estoy 100% seguro de esto, pero parece como si este método solo mira los ensamblados que estaban presentes/referenciados en tiempo de compilación, ¿alguien puede confirmar si esto es así?

He depurado esto, y esa llamada a método no encuentra realmente el conjunto que cargué justo antes de la llamada. Puede ser por algo más que me he perdido, quizás ... ¿Alguna idea?

Respuesta

20

La forma en que funcionan las cosas es un poco complicada.

GetReferencedAssemblies incluye conjuntos de referencia, conjuntos no cargados. Esto incluye:

  • todos los ensamblados de referencia en el web.config de que la aplicación (como System.Web.Mvc)
  • todo lo heredó de raíz web.config, que incluye cosas como System, System.Web y otros que usted no tiene que añadir tú mismo. (Puede echar un vistazo a la lista aquí: C:\Windows\Microsoft.Net\Framework\v4.0.30319\web.config).
    También contiene un artículo especial *, que:
  • incluye todo en la carpeta de su sitio bin

Así que ahora tomar su aplicación v1 (todo en una sola aplicación). Todo funciona porque el código de la aplicación se compila en la carpeta bin que se incluye automáticamente. Además, todas las vistas de área, etc. se encuentran en la propia aplicación, por lo que son accesibles.

Ahora en aplicación v2 (diferentes proyectos con una referencia-proj-a proj y una tarea de generación personalizada que copia las visitas a la ubicación correcta en su aplicación principal) todo sigue funcionando, ya que por defecto un proyec-to -proj references significa que el binario de la biblioteca de clase se copia en la carpeta bin de la aplicación. Entonces, según las reglas anteriores, el código de área aún se carga correctamente. El hecho de que haya configurado la ruta de salida de la biblioteca para que sea una ubicación dentro de la carpeta Áreas de su aplicación principal, en realidad no hace la diferencia; solo termina con dos copias del binario.

Ahora en la aplicación v3 (sin proj-proj ref, el conjunto de la biblioteca de área se carga manualmente) el conjunto de su biblioteca se carga demasiado tarde. Para cuando se ejecuta el código, el conjunto de conjuntos referenciados ya se ha bloqueado y ya no se puede cambiar.

No es una forma de ejecutar código y añadir elementos a la lista de ensamblados registrados: puede hacerlo utilizando el método AddReferencedAssembly cuales debe ser invocado desde un PreApplicationStartMethodAttribute method.

Por supuesto, todavía tiene que lidiar con la forma de administrar sus archivos de vista. La forma en que actualmente lo tiene configurado es prácticamente lo mismo que tener las vistas en la aplicación principal (ya que efectivamente se copian en la ubicación correcta).

+0

respuesta perfecta, gracias a mil! Sí, sé que en este momento es básicamente lo mismo que tener solo un proyecto, pero quería separarlos lentamente y ver dónde golpeo los problemas a medida que fui, en lugar de todos los problemas a la vez :) Estoy considerando tal vez compilar en las vistas como recursos en el ensamblaje y hacer mis propias implementaciones de ruta virtual/archivo, a menos que eso sea un problema de rendimiento. Como dije, solo estoy jugando en este momento para ver qué es posible y qué no. ! ¡Gracias de nuevo! – Robin

+0

Es posible que desee considerar marcar todas las vistas en el ensamblado de su área como recursos integrados y ver cómo implementar un proveedor de ruta virtual. Eso es lo que hice para lograr conjuntos de área de arrastrar y soltar que podría colocar en el directorio bin del sitio principal. –

+0

@Joshua Hayes - ¿me puede enviar su solución de áreas conectables? –

6

1 - Separa le Áreas MVC MVC en proyectos diferentes jinetes que se recopilarán en sus propias asambleas separadas

2 - Agregue esto a su clase AssemblyInfo.cs, para llamar a un método cuando la aplicación se carga

[assembly: PreApplicationStartMethod(typeof(PluginAreaBootstrapper), "Init")] 

3 - Esto es lo que el método Init parece cuando se invoca durante la 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 - Añadir una RazorViewEngine encargo

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 - Registro de sus zonas de su diferente MVC (Zona) Proyectos

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} 
       ); 
     } 
    } 
} 

código fuente y referencias adicionales se pueden encontrar aquí: http://blog.longle.io/2012/03/29/building-a-composite-mvc3-application-with-pluggable-areas

+0

Huh, mira eso, muy bien! Voy a tener que intentarlo en algún momento, ¡gracias! – Robin

+0

aprecia los puntos Robin! – LeLong37

+0

estado buscando algo como esto por días, gracias! – rossipedia

Cuestiones relacionadas