2009-09-22 10 views
6

Sappiamo tutti che l'assembly può essere interrogato per gli attributi utilizzando il metodo GetCustomAttributes. Voglio usarlo per identificare un modulo di estensione per la mia applicazione. Tuttavia, per evitare di caricare ogni assemblea preferisco un approccio difensivo:Come ottenere attributi personalizzati da un assembly che non è (realmente) caricato

  1. utilizzando Assembly.ReflectionOnlyLoadFrom per avere maggiori dettagli su un assembly (? Ha la mia ModuleAttribute)

  2. se il ModuleAttribute viene trovato, mi finalmente caricarlo usando Assembly.LoadFrom

Purtroppo sembra che non v'è alcun modo per ottenere gli attributi da un gruppo, che viene caricato nel contesto riflessione sola:

myAssembly.GetCustomAttributes(typeof(ModuleAttribute), false) 

riesce con un InvalidOperationException ("È illegale riflettere sugli attributi personalizzati di un tipo caricata tramite ReflectionOnlyGetType") e

CustomAttributeData.GetCustomAttributes(myAssembly) 

riesce con ReflectionTypeLoadException causa di assembly dipendenti non caricata.

Così come ottenere gli attributi senza

  1. inquinare il mio dominio di applicazione con i tipi inutili (forse dannoso) chiamando Assembly.LoadFrom
  2. la necessità di caricare tutti gli assembly referenziati
  3. la necessità di separata domini applicativi (dato un breve tentativo, puzzava ancora di più PITA)

?

risposta

4

Dopo aver controllato tutte le risposte e fare qualche ricerca, sembra che non v'è alcun modo per fare ciò che Voglio: controlla se un assembly è un modulo di estensione valido prima di caricarlo nel dominio dell'applicazione.

O devo caricare l'assembly che deve essere ispezionato in un altro dominio di applicazione, ispezionarlo lì e quando riesce a caricarlo di nuovo nel mio dominio dell'applicazione corrente o ho bisogno di memorizzare i metadati dell'assieme all'esterno dell'assieme stesso e fidarsi questi metadati. L'opzione 1 non è possibile a causa di restrizioni architettoniche, l'opzione due sposta semplicemente il problema ma non lo risolve.

Probabilmente l'alternativa migliore è usare il Managed Extensibility Framework, ma sfortunatamente non è così facile nella configurazione corrente.

Finisco con la certezza che non c'è nulla di "cattivo" nella directory dei moduli e il caricamento di tutto (con alcuni controlli per non superare una dimensione massima del file e non essere già caricato).

Tuttavia, grazie per le vostre idee.

0

Se fossi in te memorizzo i metadati di assembly, ad esempio, in file XML. Utilizzando questo approccio si elimina completamente la necessità di caricare qualsiasi cosa. Ed è possibile memorizzare anche le informazioni, per calcolare quale è necessario eseguire un'operazione che richiede tempo.

È possibile avere file XML pregenerati o generati al volo eseguendo la scansione di tutti gli assiemi esistenti.

+1

Non mi piace questo approccio, perché si basa su ulteriori metadati. Questo non è molto meglio di hard coding in cui deve essere localizzato un assembly con un nome di file fisso. –

+0

Si basa su metadati memorizzati nella cache, non aggiuntivi. Estrai i metadati dagli assembly e li memorizzi nella tua cache (file XML o altro). È più veloce e migliore della lettura degli assiemi. Inoltre, questo approccio è molto più estensibile perché dopo un po 'di tempo si finirà con qualcosa, non sarà possibile estrarre dall'assembly senza caricarlo. –

+1

Forse non ti capisco, ma chi sta creando la cache dei metadati? Richiede al creatore di fornire una descrizione di metadati valida o me di crearlo un po 'prima prima di provare a caricare il modulo. In entrambi i casi, qualcuno dovrà fare questo e il problema è esattamente lo stesso –

2

Hai esaminato il Microsoft AddIn Framework.

Consente il caricamento di moduli (AddIns) nei diversi domini e processi dell'app e l'interrogazione per interfaccia su dati di attributi specifici.

Potrebbe non essere quello che vuoi, ma potrebbe valere la pena dare un'occhiata.

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

E con un po 'di LINQ è possibile interrogare lo

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

MAF o anche MEF sembra essere un'opzione, sì. Sfortunatamente ciò richiederebbe un cambiamento nel design del plugin, che non è direttamente (e non obbligatorio) sviluppato da noi. –

+0

Potrebbe essere un problema se il plugin non è facilmente avvolgente –

+0

Ma suppongo che wraping sfiderebbe la tua necessità se i plugin esterni dovessero essere ricompilati da te –

4

È possibile caricare (solo riflessione) assiemi con dipendenze.

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

sarebbe risolvere i guasti durante il caricamento, ammesso che sono tutti nella stessa cartella.

+0

Puoi elaborare una risposta migliore qui? Sembra che tu abbia trovato un lavoro in giro ... Ma non posso farlo funzionare. – Vaccano

+1

Questa soluzione non è una soluzione valida ma valida. Quando l'assembly contenente i tipi richiesti non può essere scoperto nel contesto di sola riflessione, viene attivato l'evento 'ReflectionOnlyAssemblyResolve'. È quindi possibile caricare manualmente l'assieme. –

0

Se si Remoting o utilizzando una configurazione proxy, si avrà qualcosa di simile a questo:

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

Tuttavia, se hai trovato un'eccezione in tutto il dominio, probabilmente simile a questa:

{System.InvalidOperationException: È illegale riflettere sugli attributi personalizzati di un Tipo caricato tramite ReflectionOnlyGetType (vedere Assembly.ReflectionOnly) - utilizzare invece CustomAttributeData.

Ecco il codice che attiverà quello dalla prima riga.

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

Sarà necessario modificarlo per usare CustomAttributeData come questo:

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

Davvero vecchia questione, ma questo è stato possibile in quanto NET 2.0. Il problema è che tutto il carico caricato nel contesto di caricamento di sola riflessione viene implementato in modo tale da non poter innescare accidentalmente il caricamento dell'assieme nell'AppDomain corrente. Riflettere sui tipi di attributi personalizzati definiti nell'assembly è uno di questi.

È necessario utilizzare CustomAttributeData per visualizzare questi attributi personalizzati.

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

CustomAttributeData casi contengono informazioni sul l'attributo, compreso il suo tipo, parametri del costruttore e parametri denominati. Nota che i tipi nel grafico degli oggetti sono solo di riflessione, quindi se provi a fare cose con loro che potrebbero innescare il caricamento di un assembly, genererà un'eccezione familiare.

È possibile utilizzare i metadati relativi ai metadati per determinare se l'assembly riflesso si qualifica per qualsiasi esigenza.

Problemi correlati