UPDATE: Ora ho una soluzione Sono molto più contento di questo, pur non risolvendo tutti i problemi che chiedo, lascia la strada libera per farlo . Ho aggiornato la mia risposta per riflettere questo.Come precaricare tutti gli assembly distribuiti per un AppDomain
domanda iniziale
Dato un dominio App, ci sono molti luoghi diversi che la fusione (l'assemblea loader .Net) controllerà la data di assemblaggio. Ovviamente, diamo per scontata questa funzionalità e, dal momento che il sondaggio sembra essere incorporato nel .Net runtime (Assembly._nLoad
, il metodo interno sembra essere il punto di ingresso quando Reflect-Loading - e presumo che il caricamento implicito sia probabilmente coperto dallo stesso algoritmo di base), in quanto sviluppatori non sembra essere in grado di accedere a quei percorsi di ricerca.
Il mio problema è che ho un componente che esegue molta risoluzione di tipo dinamico e che deve essere in grado di assicurare che tutti gli assembly distribuiti dall'utente per un determinato AppDomain siano precaricati prima di iniziare il lavoro. Sì, rallenta l'avvio - ma i benefici che otteniamo da questo componente sono totalmente in eccesso.
L'algoritmo di caricamento di base che ho già scritto è il seguente. Esamina in profondità una serie di cartelle per qualsiasi .dll (.exes vengono esclusi al momento) e utilizza Assembly.LoadFrom per caricare la DLL se non è possibile trovare AssemblyName nel set di assiemi già caricati nell'AppDomain (questo è implementato inefficiente, ma può essere ottimizzato in seguito):
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 viene utilizzato perché ho trovato che utilizzando Load() può portare a duplicare assiemi corso di caricamento da Fusion se, quando esso esplora essa , non ne trova uno caricato da dove si aspetta di trovarlo.
Quindi, con questo in atto, tutto ciò che devo fare ora è ottenere un elenco in ordine di precedenza (dal più alto al più basso) dei percorsi di ricerca che Fusion sta per utilizzare quando cerca un assembly. Quindi posso semplicemente scorrere attraverso di loro.
Il GAC è irrilevante per questo, e non mi interessa alcun percorso fisso guidato dall'ambiente che Fusion potrebbe utilizzare: solo quei percorsi che possono essere ricavati dall'AppDomain che contengono gli assembly espressamente distribuiti per l'app.
La mia prima iterazione di questa semplice AppDomain.BaseDirectory. Funziona per servizi, form app e console.
Non funziona per un sito Web Asp.Net, tuttavia, dal momento che ci sono almeno due percorsi principali: l'AppDomain.DynamicDirectory (dove Asp.Net colloca le sue classi di pagine generate dinamicamente e tutti gli assembly che il codice di pagina Aspx riferimenti), quindi la cartella Bin del sito, che può essere rilevata dalla proprietà AppDomain.SetupInformation.PrivateBinPath.
Così ora ho un codice funzionante per i tipi di applicazioni più elementari ora (gli AppDomain ospitati da Sql Server sono un'altra storia da quando il filesystem è virtualizzato) - ma ho trovato un problema interessante un paio di giorni fa in cui questo codice semplicemente non funziona: il nUnit test runner.
Questo utilizza sia la copia ombreggiatura (quindi il mio algoritmo dovrebbe trovarsi e caricarli dalla cartella di rilascio della copia shadow, non dalla cartella bin) e imposta PrivateBinPath come relativo alla directory di base.
E ovviamente ci sono molti altri scenari di hosting che probabilmente non ho considerato; ma che deve essere valido perché altrimenti Fusion si strozzerebbe durante il caricamento degli assiemi.
Voglio smettere di sentirmi intorno e introdurre l'hack su hack per accogliere questi nuovi scenari mentre emergono - quello che voglio è, dato un AppDomain e le sue informazioni di installazione, la capacità di produrre questo elenco di cartelle che dovrei scansionare per raccogliere tutte le DLL che verranno caricate; indipendentemente da come è configurato l'AppDomain. Se Fusion può vederli come tutti uguali, allora dovrebbe essere il mio codice.
Naturalmente, potrei dover modificare l'algoritmo se .Net cambia i suoi interni - è solo una croce che dovrò sopportare. Allo stesso modo, sono felice di prendere in considerazione SQL Server e altri ambienti simili come edge case che al momento non sono supportati.
Qualche idea !?
davvero una risposta, ma molto utile per me è stato questo MSDN-articolo: http://msdn.microsoft.com/en-us/library/yx7xezcf. aspx). E il Fuslogvw.exe menzionato dovrebbe aiutare a ottenere il "perché questa DLL non è stata trovata". –
ralf.w: Mi chiedevo se potevo imbrogliare ottenendo un registro di fusione per un assemblaggio inesistente e analizzarlo per vedere tutti i posti che sta guardando. Anche se probabilmente funzionerebbe, ho la sensazione di dover attivare Fusion Logging sulla casella di destinazione (= slow); oltre al fatto che codificherei per eccezione, il che è semplicemente sbagliato! Grazie per il link all'articolo - forse un po 'di più MSDN mining potrebbe darmi la risposta ... –
Ho esattamente questo problema, saresti disposto a condividere il codice che hai ricevuto? grazie :) –