Come si scorre tra gli assembly attualmente caricati?

Ho una pagina di “diagnostica” nella mia applicazione ASP.NET che fa cose come verificare le connessioni del database, visualizzare le appSettings e ConnectionStrings correnti, ecc. Una sezione di questa pagina mostra le versioni Assembly di tipi importanti usati in tutto , ma non sono riuscito a capire come mostrare in modo efficace le versioni di TUTTI gli assembly caricati.

Qual è il modo più efficace per capire tutti gli assembly attualmente referenziati e / o caricati in un’applicazione .NET?

Nota: non sono interessato ai metodi basati su file, come iterando con * .dll in una particolare directory. Sono interessato a ciò che l’applicazione sta effettivamente usando in questo momento.

Questo metodo di estensione ottiene tutti gli assembly di riferimento, in modo ricorsivo, compresi gli assembly nidificati.

Poiché utilizza ReflectionOnlyLoad , carica gli assembly in un AppDomain separato, che ha il vantaggio di non interferire con il processo JIT.

Noterai che esiste anche un MyGetMissingAssembliesRecursive . È ansible utilizzare questo per rilevare eventuali assembly mancanti a cui si fa riferimento, ma non presenti nella directory corrente per qualche motivo. Questo è incredibilmente utile quando si usa MEF . La lista di restituzione ti fornirà sia l’assembly mancante che chi ne è proprietario (il suo genitore).

 ///  /// Intent: Get referenced assemblies, either recursively or flat. Not thread safe, if running in a multi /// threaded environment must use locks. ///  public static class GetReferencedAssemblies { static void Demo() { var referencedAssemblies = Assembly.GetEntryAssembly().MyGetReferencedAssembliesRecursive(); var missingAssemblies = Assembly.GetEntryAssembly().MyGetMissingAssembliesRecursive(); // Can use this within a class. //var referencedAssemblies = this.MyGetReferencedAssembliesRecursive(); } public class MissingAssembly { public MissingAssembly(string missingAssemblyName, string missingAssemblyNameParent) { MissingAssemblyName = missingAssemblyName; MissingAssemblyNameParent = missingAssemblyNameParent; } public string MissingAssemblyName { get; set; } public string MissingAssemblyNameParent { get; set; } } private static Dictionary _dependentAssemblyList; private static List _missingAssemblyList; ///  /// Intent: Get assemblies referenced by entry assembly. Not recursive. ///  public static List MyGetReferencedAssembliesFlat(this Type type) { var results = type.Assembly.GetReferencedAssemblies(); return results.Select(o => o.FullName).OrderBy(o => o).ToList(); } ///  /// Intent: Get assemblies currently dependent on entry assembly. Recursive. ///  public static Dictionary MyGetReferencedAssembliesRecursive(this Assembly assembly) { _dependentAssemblyList = new Dictionary(); _missingAssemblyList = new List(); InternalGetDependentAssembliesRecursive(assembly); // Only include assemblies that we wrote ourselves (ignore ones from GAC). var keysToRemove = _dependentAssemblyList.Values.Where( o => o.GlobalAssemblyCache == true).ToList(); foreach (var k in keysToRemove) { _dependentAssemblyList.Remove(k.FullName.MyToName()); } return _dependentAssemblyList; } ///  /// Intent: Get missing assemblies. ///  public static List MyGetMissingAssembliesRecursive(this Assembly assembly) { _dependentAssemblyList = new Dictionary(); _missingAssemblyList = new List(); InternalGetDependentAssembliesRecursive(assembly); return _missingAssemblyList; } ///  /// Intent: Internal recursive class to get all dependent assemblies, and all dependent assemblies of /// dependent assemblies, etc. ///  private static void InternalGetDependentAssembliesRecursive(Assembly assembly) { // Load assemblies with newest versions first. Omitting the ordering results in false positives on // _missingAssemblyList. var referencedAssemblies = assembly.GetReferencedAssemblies() .OrderByDescending(o => o.Version); foreach (var r in referencedAssemblies) { if (String.IsNullOrEmpty(assembly.FullName)) { continue; } if (_dependentAssemblyList.ContainsKey(r.FullName.MyToName()) == false) { try { var a = Assembly.ReflectionOnlyLoad(r.FullName); _dependentAssemblyList[a.FullName.MyToName()] = a; InternalGetDependentAssembliesRecursive(a); } catch (Exception ex) { _missingAssemblyList.Add(new MissingAssembly(r.FullName.Split(',')[0], assembly.FullName.MyToName())); } } } } private static string MyToName(this string fullName) { return fullName.Split(',')[0]; } } 

Aggiornare

Per rendere sicuro questo thread di codice, mettici un lock . Di default non è al momento thread-safe, in quanto fa riferimento a una variabile globale statica condivisa per fare la sua magia.

Ottenere assiemi caricati per l’ AppDomain corrente:

 var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 

Ottenere gli assembly a cui fa riferimento un altro assembly:

 var referencedAssemblies = someAssembly.GetReferencedAssemblies(); 

Si noti che se viene caricato un assembly B di riferimenti A e un assembly A, ciò non implica che anche l’assembly B sia caricato. L’Assembly B verrà caricato solo se e quando è necessario. Per questo motivo, GetReferencedAssemblies() restituisce le istanze AssemblyName anziché le istanze Assembly .