2010-08-23 6 views
30

Ich habe einen Bootstrapper, der alle Assemblies in einer ASP.NET MVC-Anwendung durchsucht, um Typen zu finden, die eine IBootstrapperTask Schnittstelle implementieren, und registriert sie dann mit ein IOC Contrainer. Die Idee ist, dass Sie Ihre IBootstrapperTasks buchstäblich überall hinstellen und Ihre Projekte nach Belieben organisieren können.ASP.NET - AppDomain.CurrentDomain.GetAssemblies() - Assemblys fehlen nach AppDomain Neustart

-Code für Bootstrapper:

public class Bootstrapper 
{ 
    static Bootstrapper() 
    { 
     Type bootStrapperType = typeof(IBootstrapperTask); 

     IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies(); 

     List<Type> tasks = new List<Type>(); 

     foreach (Assembly assembly in assemblies) 
     { 
      var types = from t in assembly.GetTypes() 
         where bootStrapperType.IsAssignableFrom(t) 
          && !t.IsInterface && !t.IsAbstract 
         select t; 

      tasks.AddRange(types); 
     } 

     foreach (Type task in tasks) 
     { 
      if (!IocHelper.Container().Kernel.HasComponent(task.FullName)) 
      { 
       IocHelper.Container().AddComponentLifeStyle(
        task.FullName, task, LifestyleType.Transient); 
      } 
     } 
    } 

    public static void Run() 
    { 
     // Get all registered IBootstrapperTasks, call Execute() method 
    } 
} 

Nach einer vollständigen Build AppDomain.CurrentDomain.GetAssemblies() gibt alle Baugruppen in meiner Lösung (einschließlich aller GAC jemandes aber das stört mich nicht).

Wenn jedoch die AppDomain neu gestartet wird, oder ich ‚Bounce‘ die Web.Config-Datei (das Hinzufügen eines Raumes und speichern), wird der statische Konstruktor erneut ausführen, aber wenn AppDomain.CurrentDomain.GetAssemblies() genannt wird, meisten Baugruppen fehlen einschließlich desjenigen, der meine IBootstrapperTask-Typen enthält.

Wie kann ich dieses Problem umgehen? Ich denke ich könnte System.IO das Verzeichnis/bin und alle DLLs manuell dort einspielen, aber würde dies möglichst vermeiden, oder ist das der einzige Weg? Wende ich den richtigen allgemeinen Ansatz dazu?

Dies ist eine ASP.NET MVC 2.0-Anwendung unter .NET 4.0, ich bekomme dieses Problem mit dem integrierten Visual Studio 2010 Cassini-Webserver und mit IIS7.0 im integrierten Pipeline-Modus unter Windows Server 2008.


Edit: ich auf diese kam gerade sO Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies posten, die sagt der AppDomain nur die Baugruppen lädt wie sie benötigt werden (zB wenn eine Methode/Klasse von dieser Versammlung wird zuerst genannt.). Ich denke, das würde erklären, warum die Assemblys auf AppDomain.CurrentDomain.GetAssemblies() fehlen, da der Bootstrapper sehr früh läuft.

Ich habe bemerkt, wenn ich einen Anruf auf ‚etwas‘ von der fehlenden Versammlung vor dem Bootstrapper zB platziert:

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     MyApp.MissingAssembly.SomeClass someClass = 
      new MyApp.MissingAssembly.SomeClass(); 

     Bootstrapper.Run(); 
    } 
} 

... es scheint, das Problem zu beheben, aber es ist ein bisschen wie ein Hack.

+0

Nizza Frage und Antwort:

Ich habe meine Assembly-Locator-Code auf die folgenden neu geschrieben! Hat eine Tour von dir Blog - Sie schreiben ein paar gute Sachen. –

Antwort

50

Ich schaute durch den ASP.NET MVC 2.0 Quellcode und sah nach, wie AreaRegistration.RegisterAllAreas(); implementiert ist. Diese Zeile wird normalerweise in die Global.asax-Methode Application_Start() eingefügt und durchsucht intern alle Assemblys nach Typen, die den abstrakten Typ AreaRegistration implementieren. Das ist irgendwie das Verhalten, nach dem ich suche.

Es scheint RegisterAllAreas() macht einen Aufruf an BuildManager.GetReferencedAssemblies(), auch wenn es gut genug für MVC, dann ist es gut genug für mich :-)

Ich habe einige Experimente und BuildManager.GetReferencedAssemblies getan() wird selbst wählen adhoc nach oben, zufällige DLLs in den Ordner/bin fallen gelassen, auch ohne Referenzen auf Projekte in der Visual Studio-Lösung. So erscheint es weitaus zuverlässiger als AppDomain.Current.GetAssemblies().

using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Web; 
using System.Web.Compilation; 

public static class AssemblyLocator 
{ 
    private static readonly ReadOnlyCollection<Assembly> AllAssemblies; 
    private static readonly ReadOnlyCollection<Assembly> BinAssemblies; 

    static AssemblyLocator() 
    { 
     AllAssemblies = new ReadOnlyCollection<Assembly>(
      BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList()); 

     IList<Assembly> binAssemblies = new List<Assembly>(); 

     string binFolder = HttpRuntime.AppDomainAppPath + "bin\\"; 
     IList<string> dllFiles = Directory.GetFiles(binFolder, "*.dll", 
      SearchOption.TopDirectoryOnly).ToList(); 

     foreach (string dllFile in dllFiles) 
     { 
      AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllFile); 

      Assembly locatedAssembly = AllAssemblies.FirstOrDefault(a => 
       AssemblyName.ReferenceMatchesDefinition(
        a.GetName(), assemblyName)); 

      if (locatedAssembly != null) 
      { 
       binAssemblies.Add(locatedAssembly); 
      } 
     } 

     BinAssemblies = new ReadOnlyCollection<Assembly>(binAssemblies); 
    } 

    public static ReadOnlyCollection<Assembly> GetAssemblies() 
    { 
     return AllAssemblies; 
    } 

    public static ReadOnlyCollection<Assembly> GetBinFolderAssemblies() 
    { 
     return BinAssemblies; 
    } 
} 
+0

Ich stieß auf dieses Problem und verwendete Ihre Lösung. Stimme ab! Vielen Dank! – ajma

+0

Vielen Dank! Ich bin auch in diesem Problem stecken geblieben. Du sparst meine Zeit. – Azat

1

Sieht aus wie Sie Ihr eigenes Problem gelöst haben.

Edit: Persönlich würde ich tatsächlich die Baugruppen einzeln auflisten, laden Sie sie und suchen Sie nach der Schnittstelle. Führen Sie es auf der Basis von Dateien durch, anstatt was die AppDomain tut.

+0

Meinst du das letzte Codebeispiel MyApp.MissingAssembly.SomeClass usw.? Wenn dies der Fall ist, ist es keine wirkliche Lösung, ich müsste Dummy-Aufrufe vor Bootstrapper.Run() zu verschiedenen Assemblys hinzufügen. –

+0

Ich denke, er meint, dass Sie die Dateien durchgehen und sie explizit laden sollten. Dann wirst du nicht davon abhängen, ob du es zufällig in einen Anruf geladen hast oder nicht. –