So laden Sie alle bereitgestellten Assemblies für eine Anwendungsdomäne vorab
UPDATE: ich habe jetzt eine Lösung ich bin viel glücklicher mit, dass zwar nicht die Lösung all der Probleme, die ich Fragen über, es lässt den Weg frei, dies zu tun. Ich habe aktualisiert, meine eigenen Antworten zu reflektieren.
Ursprüngliche Frage
Gegeben, eine App-Domäne, es gibt viele verschiedene Orte, dass die Fusion (die .Net-assembly-loader) wird die Sonde für eine bestimmte Baugruppe. Offensichtlich nehmen wir diese Funktionalität für selbstverständlich, und, da die gesuchten zu sein scheint, eingebettet in die .Net-runtime (Assembly._nLoad
interne Methode scheint der entry-Punkt bei der Reflect-Laden - und ich gehe davon aus, dass implizite laden ist wahrscheinlich unter den gleichen zugrunde liegenden Algorithmus), als Entwickler, die wir nicht scheinen, um zu gewinnen, Zugang zu den Suchpfaden.
Mein problem ist, dass ich eine Komponente, das eine Menge von dynamischen Typ Auflösung, und die muss in der Lage sein, um sicherzustellen, dass alle vom Benutzer bereitgestellten Baugruppen für eine bestimmte Anwendungsdomäne sind bereits geladen, bevor es mit seiner Arbeit beginnt. Ja, es verlangsamt Systemstart - aber die Vorteile, die wir von dieser Komponente völlig outweight.
Den basic-loading-Algorithmus habe ich bereits geschrieben, ist wie folgt. Es tief scannt eine Reihe von Ordnern für jeden .dll (.exes sind ausgeschlossen im moment), und benutzt die Versammlung.LoadFrom um die dll zu laden, wenn es AssemblyName nicht gefunden werden kann in dem Satz von Assemblys bereits geladen wird in die Anwendungsdomäne (in diesem implementiert ist ineffizient, aber es kann später optimiert):
void PreLoad(IEnumerable<string> paths)
{
foreach(path p in paths)
{
PreLoad(p);
}
}
void PreLoad(string p)
{
//all try/catch blocks are elided for brevity
string[] files = null;
files = Directory.GetFiles(p, "*.dll", SearchOption.AllDirectories);
AssemblyName a = null;
foreach (var s in files)
{
a = AssemblyName.GetAssemblyName(s);
if (!AppDomain.CurrentDomain.GetAssemblies().Any(
assembly => AssemblyName.ReferenceMatchesDefinition(
assembly.GetName(), a)))
Assembly.LoadFrom(s);
}
}
LoadFrom verwendet wird, denn ich habe festgestellt, dass mit Load() kann führen zu doppelten Assemblys geladen werden, die durch die Fusion, wenn es Sonden, ist es nicht geladen, von wo erwartet er es zu finden.
So, in diesem Ort, alles, was ich jetzt tun müssen, ist, um eine Liste in der Rangfolge (von der höchsten zur niedrigsten) der Suchpfade, die Fusion zu gehen, um mithilfe bei der Suche nach einer assembly. Dann kann ich einfach Durchlaufen.
GAC ist irrelevant für diese, und ich bin nicht daran interessiert, in jeder Umgebung-driven Feste Wege, die Fusion verwenden könnte - nur die Pfade, die zusammengetragen werden können, von der AppDomain, in denen Baugruppen ausdrücklich bereitgestellt für die app.
Meine erste iteration dieses einfach Anwendungsdomäne verwendet.BaseDirectory. Dies funktioniert für Dienstleistungen in form von apps und Konsolen-apps.
Funktioniert es nicht für eine Asp.Net website, jedoch, da es mindestens zwei Haupt-Standorten der Anwendungsdomäne.DynamicDirectory (wo Asp.Net stellen Sie die dynamisch generierte Seite Klassen und Assemblys, die die Aspx-Seite code-Referenzen), und dann wird die Website in den Ordner Bin -, die entdeckt werden kann, von der Anwendungsdomäne.SetupInformation.PrivateBinPath Eigenschaft.
So, ich habe jetzt funktionierenden code für die grundlegenden Arten von apps jetzt (Sql Server-gehostet AppDomains sind eine andere Geschichte, da das Dateisystem virtualisiert) - aber ich bin auf ein Interessantes Problem vor ein paar Tagen, wo dieser code funktioniert einfach nicht: der nUnit-test-runner.
Diese nutzt sowohl Schatten-Kopieren (also mein Algorithmus werden müssten, entdecken und laden Sie aus dem Schatten-Kopie-drop-Ordner, nicht die aus dem bin-Ordner) und es setzt den PrivateBinPath als relativ zum Basis-Verzeichnis.
Und natürlich gibt es viele andere hosting-Szenarien, die ich wahrscheinlich noch nicht überlegt, aber die muss gültig sein, weil sonst die Fusion würde choke beim laden der Baugruppen.
Ich will aufhören, das Gefühl um und die Einführung von hack nach hack, um Platz für diese neuen Szenarien, wie Sie auftauchen, was ich will, ist, angesichts einer Anwendungsdomäne und Ihre setup-Informationen, die Fähigkeit zu produzieren, diese Liste der Ordner, die ich Scannen sollte, um abholen alle DLLs, die geladen werden; unabhängig davon, wie die AppDomain ist setup. Wenn Fusion können Sie sehen, wie alle die gleichen, dann sollte mein code.
Natürlich, ich könnte haben, zu ändern, den Algorithmus, wenn .Netto verpasst seiner Interna - das ist nur ein Kreuz ich werde zu tragen haben. Ebenso, ich bin glücklich, zu prüfen, SQL Server und alle anderen ähnlichen Umgebungen Rand-Fällen, die weiterhin nicht unterstützt für jetzt.
Irgendwelche Ideen!?
InformationsquelleAutor der Frage Andras Zoltan | 2010-06-11
Du musst angemeldet sein, um einen Kommentar abzugeben.
Habe ich jetzt in der Lage zu bekommen, etwas, das viel näher an einer endgültigen Lösung, außer, dass es noch nicht die Verarbeitung der private bin-Pfad richtig. Ich habe ersetzt meine zuvor live code mit dieser und löste auch ein paar böse runtime-bugs, die ich hatte, in die Schnäppchen (die dynamische Kompilierung von C# - code referenzieren, viel zu viele dlls).
Die goldene Regel, die ich seither entdeckt habe, ist verwenden Sie immer die load-Kontextnicht der LoadFrom-Kontext, da die Last der Kontext wird immer die erste Stelle sein .Netto sieht bei der Durchführung einer natürlichen binden. Daher, wenn Sie die LoadFrom-Kontext, Sie erhalten nur einen Treffer, wenn Sie tatsächlich laden Sie es von der gleichen Stelle, wäre es natürlich binden Sie es aus - das ist nicht immer einfach.
Diese Lösung funktioniert sowohl bei web-Anwendungen unter Berücksichtigung der bin-Ordner Unterschied zu "standard" - apps. Es kann leicht erweitert werden, um Platz für die
PrivateBinPath
problem, sobald ich eine zuverlässige Griff auf, genau so, wie es ist zu Lesen(!)Zuerst haben wir die Methode verwendet, um die von uns gewählte 'app-Ordner'. Dies sind die Orte, an denen die Benutzer bereitgestellt werden Assemblys bereitgestellt wurden. Es ist ein IEnumerable, weil der
PrivateBinPath
edge-Fall (es kann werden eine Reihe von Standorten), aber in der Praxis ist es immer nur ein Ordner im moment:Die nächste Methode ist
PreLoadDeployedAssemblies()
die aufgerufen wird, bevor etwas zu tun (hier aufgelistet alsprivate static
- in meinem code dies ist ein Auszug aus einer viel größeren statischen Klasse, die öffentlichen Endpunkte, die immer Auslöser dieser code ausgeführt werden, bevor etwas zu tun für die erste Zeit.Endlich gibt es das Fleisch und die Knochen. Das wichtigste hier ist es, eine assembly-Datei und erhalten es ist die assembly-namedie Sie dann weitergeben zu
Assembly.Load(AssemblyName)
- und nicht, umLoadFrom
.Ich bisher dachte, dass
LoadFrom
zuverlässiger war, und das mussten Sie manuell gehen und finden Sie die temporäre Asp.Net Ordner in web-apps. Sie nicht. Alles, was Sie wissen, ist der name einer assembly, die Sie wissen sollten auf jeden Fall geladen werden - und übergeben es anAssembly.Load
. Nachdem alle, das ist praktisch das, was .Net Referenz laden von Routinen tun 🙂Ebenso, dieser Ansatz funktioniert gut mit benutzerdefinierten assembly Sondieren umgesetzt durch erhängen aus der
AppDomain.AssemblyResolve
Veranstaltung als gut: die app Erweitern, bin-Ordner alle plugin-container Ordner, die Sie haben können, so dass Sie gescannt. Wahrscheinlich haben Sie bereits behandeltAssemblyResolve
Ereignis irgendwie um sicherzustellen, dass Sie geladen werden, wenn die normale überprüfung fehlschlägt, so funktioniert alles wie vorher.InformationsquelleAutor der Antwort Andras Zoltan
Dies ist, was ich Tue:
InformationsquelleAutor der Antwort Andrew Bullock
Haben Sie versucht, Blick auf die Montage.GetExecutingAssembly().Lage? Das sollte geben Sie den Pfad der assembly, wo der code ausgeführt wird. In der NUnit Fall würde ich erwarten, dass, wo die Versammlungen waren Schatten kopiert.
InformationsquelleAutor der Antwort Andy