2008-12-20 19 views
107

Tengo una página de "diagnóstico" en mi aplicación ASP.NET que hace cosas como verificar la (s) conexión (es) de la base de datos, mostrar la configuración de la aplicación actual y ConnectionStrings, etc. Una sección de esta página muestra las versiones de ensamblaje de tipos importantes utilizado en todas partes, pero no pude encontrar la manera de mostrar efectivamente las versiones de TODOS los ensamblajes cargados.¿Cómo recorre los ensamblados cargados actualmente?

¿Cuál es la forma más eficaz de averiguar todo actualmente referenciados y/o cargados Asambleas en una aplicación .NET?

Nota: No me interesan los métodos basados ​​en archivos, como iterar a través de * .dll en un directorio en particular. Estoy interesado en lo que la aplicación es en realidad usando en este momento.

Respuesta

16

Este método de extensión obtiene todos los ensamblados a los que se hace referencia, recursivamente, incluidos los ensamblajes anidados.

Como usa ReflectionOnlyLoad, carga los ensamblajes en un Dominio de aplicación separado, que tiene la ventaja de no interferir con el proceso JIT.

Notarás que también hay un MyGetMissingAssembliesRecursive. Puede usar esto para detectar cualquier conjunto faltante al que se haga referencia, pero que no esté presente en el directorio actual por algún motivo. Esto es increíblemente útil cuando se usa MEF. La lista de retorno le dará tanto el ensamblaje que falta, y quién lo posee (su padre).

/// <summary> 
///  Intent: Get referenced assemblies, either recursively or flat. Not thread safe, if running in a multi 
///  threaded environment must use locks. 
/// </summary> 
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<string, Assembly> _dependentAssemblyList; 
    private static List<MissingAssembly> _missingAssemblyList; 

    /// <summary> 
    ///  Intent: Get assemblies referenced by entry assembly. Not recursive. 
    /// </summary> 
    public static List<string> MyGetReferencedAssembliesFlat(this Type type) 
    { 
     var results = type.Assembly.GetReferencedAssemblies(); 
     return results.Select(o => o.FullName).OrderBy(o => o).ToList(); 
    } 

    /// <summary> 
    ///  Intent: Get assemblies currently dependent on entry assembly. Recursive. 
    /// </summary> 
    public static Dictionary<string, Assembly> MyGetReferencedAssembliesRecursive(this Assembly assembly) 
    { 
     _dependentAssemblyList = new Dictionary<string, Assembly>(); 
     _missingAssemblyList = new List<MissingAssembly>(); 

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

    /// <summary> 
    ///  Intent: Get missing assemblies. 
    /// </summary> 
    public static List<MissingAssembly> MyGetMissingAssembliesRecursive(this Assembly assembly) 
    { 
     _dependentAssemblyList = new Dictionary<string, Assembly>(); 
     _missingAssemblyList = new List<MissingAssembly>(); 
     InternalGetDependentAssembliesRecursive(assembly); 

     return _missingAssemblyList; 
    } 

    /// <summary> 
    ///  Intent: Internal recursive class to get all dependent assemblies, and all dependent assemblies of 
    ///  dependent assemblies, etc. 
    /// </summary> 
    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]; 
    } 
} 

actualización

Para hacer este hilo código de seguridad, poner un lock alrededor de ella. Actualmente no es seguro para subprocesos, ya que hace referencia a una variable global estática compartida para hacer su magia.

+0

Acabo de volver a escribir esto para que sea seguro para subprocesos, por lo que se puede llamar desde muchos subprocesos diferentes al mismo tiempo (no estoy seguro de por qué querría eso, pero bueno, es más seguro). Avísame si quieres que publique el código. – Contango

+1

también podría publicar su versión actualizada también – Michael

+1

@Contango ¿Podría publicar su versión de Thread safe, o si ha escrito un blog al respecto, publíquelo? – Robert

176

Primeros ensamblados cargados para la corriente AppDomain:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 

Obtención de los ensamblados de referencia por otro conjunto:

var referencedAssemblies = someAssembly.GetReferencedAssemblies(); 

Tenga en cuenta que si se carga montaje referencias de ensamblaje A B y el conjunto A, que hace no implica que el conjunto B también esté cargado. El ensamblaje B solo se cargará cuando sea necesario. Por ese motivo, GetReferencedAssemblies() devuelve AssemblyName instancias en lugar de Assembly instancias.

+1

Bueno, necesito algo como esto: dada una solución .net, quiero encontrar todos los ensamblajes a los que se hace referencia en todos los proyectos. Ideas? –

+0

Tenga en cuenta que ambos métodos solo muestran los archivos DLL que realmente se usan. Obviamente, no tiene sentido tener referencias en soluciones que no se usan, pero esto puede ser confuso cuando alguien está tratando de escanear especulativamente TODOS los ensamblajes. Todas las asambleas pueden no aparecer. – Pompair

+0

@Kumar Vaibhav He agregado una respuesta que hace precisamente eso. – Contango

Cuestiones relacionadas