2009-09-22 3 views
6

Wir wissen alle, dass die Assembly mithilfe der GetCustomAttributes-Methode nach Attributen abgefragt werden kann. Ich möchte damit ein Erweiterungsmodul für meine Anwendung identifizieren. Um jedoch zu vermeiden jede Montag Laden ziehe ich einen defensiven Ansatz:So erhalten Sie benutzerdefinierte Attribute von einer Assembly, die nicht (wirklich) geladen wurde

  1. Assembly.ReflectionOnlyLoadFrom mit mehr Details über eine Assembly zu bekommen (? Hat es meine ModuleAttribute)

  2. wenn die ModuleAttribute gefunden, ich wird es schließlich lädt mit Assembly.LoadFrom

Leider scheint es, dass es keine Möglichkeit gibt, die Attribute aus einer Baugruppe zu bekommen, dass in die reflexions nur Kontext geladen wird:

myAssembly.GetCustomAttributes(typeof(ModuleAttribute), false) 

schlägt mit einer InvalidOperationException („Es ist illegal auf den benutzerdefinierten Attributen einen Typs über ReflectionOnlyGetType geladen zu reflektieren“) und

CustomAttributeData.GetCustomAttributes(myAssembly) 

mit Reflection wegen abhängigen Assemblys versagt nicht geladen werden.

So, wie die Attribute zu erhalten, ohne

  1. meine Anwendungsdomäne mit nutzlosen (möglicherweise schädliche) Arten verschmutzen durch Assembly.LoadFrom Aufruf
  2. die Notwendigkeit, alle referenzierten Assemblys für separate
  3. die Notwendigkeit zu laden Anwendungsdomänen (gab es einen kurzen Versuch, roch nach noch mehr PITA)

?

Antwort

4

Nachdem alle Antworten überprüft und einige mehr Forschung zu tun, so scheint es, dass es einfach keine Möglichkeit, was zu tun ist Ich möchte: Überprüfen Sie, ob eine Assembly ein gültiges Erweiterungsmodul ist, bevor Sie es in die Anwendungsdomäne laden.

Entweder muss ich die Assembly laden, die in eine andere Anwendungsdomäne untersucht werden soll, dort überprüfen und wenn es erfolgreich wieder in meine aktuelle Anwendungsdomäne geladen wird oder Metadaten der Assembly außerhalb der Assembly selbst speichern und vertrauen diese Metadaten. Option eins ist aufgrund von Architektureinschränkungen nicht möglich, Option zwei verschiebt das Problem nur, löst es aber nicht.

Wahrscheinlich ist die beste Alternative, das Managed Extensibility Framework zu verwenden, aber das ist im aktuellen Setup leider nicht so einfach.

Ich vertraue darauf, dass es nichts "schlechtes" im modules-Verzeichnis gibt und alles geladen wird (mit einigen Überprüfungen, die eine maximale Dateigröße nicht überschreiten und nicht bereits geladen werden).

Trotzdem, vielen Dank für Ihre Ideen.

0

Wenn ich Sie wäre, würde ich Assemblymetadaten beispielsweise in XML-Dateien zwischenspeichern. Mit diesem Ansatz wird die Notwendigkeit, etwas zu laden, vollständig eliminiert. Und Sie können sogar Informationen speichern, um zu berechnen, welche zeitaufwendige Operation erforderlich ist.

Sie können XML-Dateien vorgeneriert oder während des Betriebs generieren lassen, indem Sie alle vorhandenen Baugruppen scannen.

+1

Dieser Ansatz mag diesen Ansatz nicht, da er auf zusätzlichen Metadaten beruht. Das ist nicht viel besser als hartes Codieren, in welchem ​​Verzeichnis eine Assembly mit festem Dateinamen gefunden werden muss. –

+0

Es beruht auf zwischengespeicherten Metadaten, nicht auf zusätzlichen. Sie extrahieren Metadaten aus Assemblys und speichern sie in Ihrem Cache (XML-Dateien oder was auch immer). Es ist schneller und besser als Baugruppen zu lesen. Außerdem ist dieser Ansatz wesentlich erweiterbarer, da Sie nach einiger Zeit etwas finden werden, das Sie nicht aus der Assembly extrahieren können, ohne es zu laden. –

+1

Vielleicht bekomme ich dich nicht, aber wer erstellt den Metadaten-Cache? Es erfordert entweder den Ersteller, eine gültige Metadatenbeschreibung bereitzustellen, oder ich muss etwas früher erstellen, bevor ich versuche, das Modul zu laden. In beiden Fällen muss jemand dies tun und das Problem ist genau das gleiche –

2

Haben Sie sich das Microsoft AddIn Framework angeschaut.

Ermöglicht das Laden von Modulen (AddIns) in die verschiedenen App-Domänen und -Prozesse sowie die Abfrage nach spezifischen Attributdaten über die Schnittstelle.

Es könnte nicht sein, was Sie wollen, aber könnte einen Blick wert sein.

http://msdn.microsoft.com/en-us/library/bb384200.aspx

string[] warnings = AddInStore.Update(Environment.CurrentDirectory); 
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(YourType), Environment.CurrentDirectory) 

AddInEnvironment addInEnvironment = new AddInEnvironment(AppDomain.CurrentDomain); 
YourType module = tokens[0].Activate<YourType>(addInEnvironment); 

Und mit ein bisschen LINQ können Sie abfragen, es

AddInToken myToken = tokens.FirstOrDefault(currentAddInToken => currentAddInToken.Name.Equals(moduleName)) 
+0

MAF oder sogar MEF scheint eine Option zu sein, ja. Leider würde dies eine Änderung im Design des Plugins erfordern, die nicht direkt (und nicht zwingend) von uns entwickelt wurde. –

+0

Es könnte ein Problem sein, wenn das Plugin nicht leicht wrapable –

+0

Aber ich denke, Wrapping es würde Ihr Bedürfnis trotzen, wenn die externen Plugins von Ihnen neu kompiliert werden müssen –

4

Es ist möglich, Baugruppen mit Abhängigkeiten zu laden (nur Reflektion).

AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => Assembly.ReflectionOnlyLoad(e.Name);

würden Ausfälle behoben beim Laden, vorausgesetzt, sie alle im selben Ordner befinden.

+0

Können Sie mit einer besseren Antwort hier erarbeiten? Es klingt, als hättest du eine Arbeit gefunden ... Aber ich kann es nicht funktionieren lassen. – Vaccano

+1

Dies ist keine Problemumgehung, sondern eine gültige Lösung. Wenn die Assembly, die die erforderlichen Typen enthält, nicht im Reflektionskontext gefunden werden kann, wird das Ereignis "ReflectionOnlyAssemblyResolve" ausgelöst. Sie können die Baugruppe dann manuell laden. –

0

Wenn Sie einen Proxy-Konfiguration remoting oder verwenden, werden Sie etwas Ähnliches wie dieses:

AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => OnReflectionOnlyResolve(e, directory); 

jedoch das sieht aus wie wenn Sie eine Ausnahme für die Domain bekommen, ist es wahrscheinlich:

{System.InvalidOperationException: Es ist illegal, über die benutzerdefinierten Attribute eines über ReflectionOnlyGetType geladenen Typs nachzudenken (siehe Assembly.ReflectionOnly) - verwenden Sie stattdessen CustomAttributeData.

Hier ist der Code, der das aus der 1. Zeile auslösen wird.

object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false); 
return attributes.Length == 0 ? "" : ((AssemblyTitleAttribute) attributes[0]).Title; 

Sie müssen es ändern Custom wie folgt zu verwenden:

foreach (CustomAttributeData cad in assembly.GetCustomAttributesData().Where(a => a.AttributeType == typeof (AssemblyTitleAttribute))) 
    return cad.ConstructorArguments.FirstOrDefault().Value as String; 
return String.Empty; 
1

Wirklich alte Frage, aber das hat sich seit .NET 2.0 möglich. Das Problem ist, dass alles, was in den reflection-only load-Kontext geladen wird, implementiert ist, sodass Sie nicht versehentlich das Laden der Assembly in die aktuelle AppDomain auslösen können. Das Reflektieren über die in der Assembly definierten benutzerdefinierten Attributtypen ist einer davon.

Sie müssten CustomAttributeData verwenden, um diese benutzerdefinierten Attribute anzuzeigen.

var assy = Assembly.ReflectionOnlyLoadFrom("MuhPlugin.dll"); 
// gets assembly-level attributes, but you get the idea 
var attrs = CustomAttributeData.GetCustomAttributes(assy); 

CustomAttributeData Instanzen enthalten Informationen über das Attribut, einschließlich der Art, Konstruktorparameter und benannte Parameter. Beachten Sie, dass die Typen im Objektdiagramm nur eine Reflektionsoption sind. Wenn Sie also versuchen, mit Objekten zu arbeiten, die das Laden einer Assembly auslösen, wird die bekannte Ausnahme ausgelöst.

Sie können diese Metadaten zu Ihren Metadaten verwenden, um festzustellen, ob die reflektierte Baugruppe für Ihre Anforderungen geeignet ist.

Verwandte Themen