2010-11-22 10 views
13

Obecnie eksperymentuję z dynamicznie ładowanymi obszarami z ASP.NET MVC 3 RC. Widziałem to w wielu miejscach, że nie jest to przeznaczenie obszarów i (przynajmniej pre-MVC 2) niemożliwe, na przykład here.ASP.NET MVC 3 RC AreaRegistration.RegisterAllAreas() i dynamicznie ładowane złożenia

Ale wciąż! Powinno być możliwe uruchomienie go, prawda? Stworzyłem rozwiązanie, dodałem projekt MVC 3, dodałem obszar i trochę treści. Wszystko działa dobrze. Teraz utworzyłem nowy projekt biblioteki klas (w tym samym rozwiązaniu), dodałem do niego odnośnik z projektu MVC i zacząłem przenosić części związane z obszarem do biblioteki. Zmieniono katalog wyjściowy projektu bibliotecznego na folder obszaru projektu MVC i upewniono się, że widoki i ich plik web.config zostaną skopiowane do folderu wyjściowego.

Po lekturze tak dużo o tym, jak nie można mieć zewnętrznych obszarów, to było trochę zaskakujące, że zadziałało. Naprawdę nie ma problemu! Problem zaczyna się, gdy usuwam odwołanie między projektami, a zamiast tego ładuję bibliotekę w kodzie. (Przed wywołaniem AreaRegistration.RegisterAllAreas().) Teraz to nie działa. W ogóle.

Poświęciłem trochę w źródle dla MVC 3, a problem wydaje się być z BuildManager.GetReferencedAssemblies(), który jest używany do uzyskania złożeń, aby szukać implementacji AreaRegistration.

Nie jestem w 100% pewny, ale wygląda na to, że ta metoda analizuje tylko zespoły, które były obecne/odniesienia podczas kompilacji, czy ktoś może potwierdzić, czy tak jest w rzeczywistości?

Mam debugowane przez to i to wywołanie metody rzeczywiście nie znajduje zespołu, który wczytałem tuż przed wywołaniem. Być może z powodu czegoś, co przegapiłem może .. Jakieś pomysły?

Odpowiedz

20

Sposób w jaki działa, jest nieco skomplikowany.

GetReferencedAssemblies zawiera zespoły referencyjne, niezaładowane zespoły. Obejmuje to:

  • wszystkie zespoły odwołuje się web.config ciebie aplikacji (takich jak System.Web.Mvc)
  • wszystko odziedziczone web.config korzeniowego, który obejmuje takie rzeczy jak System, System.Web i innym, że nie trzeba dodawać siebie. (Możesz przejrzeć listę tutaj: C:\Windows\Microsoft.Net\Framework\v4.0.30319\web.config).
    Zawiera także specjalny * element, który:
  • zawiera wszystko w folderze witryny bin

Więc teraz zabrać aplikacji V1 (wszystko w jednej aplikacji). Wszystko działa, ponieważ kod aplikacji zostanie skompilowany do folderu bin, który zostanie automatycznie dołączony. Ponadto wszystkie widoki obszaru itp. Znajdują się w samej aplikacji, dzięki czemu są dostępne.

Teraz w app v2 (inny projekt z odniesieniem proj-to-proj i niestandardowego zadania budowy, który kopiuje widok na właściwym miejscu w głównej aplikacji) wszystko nadal działa, ponieważ domyślnie proj-do -proj oznacza, że ​​plik binarny biblioteki klas zostanie skopiowany do folderu bin aplikacji. Tak więc zgodnie z powyższymi zasadami, numer kierunkowy nadal jest poprawnie ładowany. Fakt, że ustawiłeś ścieżkę wyjściową biblioteki, aby była jakąś lokalizacją w folderze Obszary głównej aplikacji, nie ma znaczenia - skończysz z dwiema kopiami binarnymi.

Teraz w aplikacji v3 (bez funkcji proj-proj, zespół biblioteki obszaru jest ładowany ręcznie) zestaw biblioteki zostanie załadowany zbyt późno. Do czasu uruchomienia kodu zestaw odwołanych złożeń został już zablokowany i nie można go już zmienić.

Jest na to sposób, aby uruchomić kod i dodać elementy do listy zarejestrowanych zespołów: można to zrobić przy użyciu metody AddReferencedAssembly który musi być wywołany z PreApplicationStartMethodAttribute method.

Oczywiście nadal musisz sobie radzić z zarządzaniem plikami widoku. Sposób, w jaki ją konfigurujesz, jest prawie taki sam jak widok w głównej aplikacji (ponieważ są one skutecznie kopiowane do właściwej lokalizacji).

+0

Doskonała odpowiedź, dziękuję mil! Tak, wiem, że w tej chwili jest w zasadzie taki sam, jak w przypadku jednego projektu, ale chciałem je rozdzielić powoli i zobaczyć, gdzie trafiłem problemy, gdy jechałem, zamiast wszystkich problemów naraz :) Jestem biorąc pod uwagę możliwość kompilowania widoków jako zasobów w złożeniu i tworzenia własnych wirtualnych ścieżek/plików, chyba że byłby to zbyt duży problem z wydajnością. Jak już powiedziałem, po prostu bawię się w tej chwili, aby zobaczyć, co jest możliwe, a co nie. ! Jeszcze raz dziękuję! – Robin

+0

Być może spodoba Ci się zaznaczenie wszystkich widoków w zespole obszaru jako zasobów osadzonych i przyjrzenie się implementacji dostawcy ścieżki wirtualnej. Właśnie to zrobiłem, aby uzyskać zespoły przeciągania i upuszczania, które mogłem po prostu umieścić w katalogu bin głównej strony. –

+0

@Joshua Hayes - czy możesz przesłać mi rozwiązanie w zakresie wtyczek? –

6

1 - oddziel wy MVC obszarów na differrent projektów MVC zostać skompilowany do własnych zespołów oddzielne

2 - dodaj to do klasy AssemblyInfo.cs, aby wywołać metodę, gdy aplikacja jest ładowany

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

3 - Oto, co metoda Init wygląda, gdy jest wywoływana podczas obciążenia

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 - Dodawanie niestandardowego RazorViewEngine

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 - zgłoś swoje obszary z różnych MVC (obszar) Realizacje

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

źródłowego i dodatkowe odniesienia może można znaleźć tutaj: http://blog.longle.io/2012/03/29/building-a-composite-mvc3-application-with-pluggable-areas

+0

Huh, spójrz na to, bardzo miło! W pewnym momencie będę musiał spróbować, dziękuję! – Robin

+0

docenić punkty Robin! – LeLong37

+0

Szukałem czegoś takiego przez wiele dni, dzięki! – rossipedia

Powiązane problemy