2008-12-20 11 views
107

Mam stronę "diagnostyki" w mojej aplikacji ASP.NET, która wykonuje takie czynności, jak weryfikacja połączenia z bazą danych, wyświetlanie bieżących ustawień aplikacji i śladów ConnectionString itp. Sekcja na tej stronie wyświetla wersje ważnych typów zespołów używane przez cały czas, ale nie mogłem wymyślić, jak skutecznie pokazać wersje WSZYSTKIE załadowanych zestawów.W jaki sposób przechodzisz przez aktualnie wczytane złożenia?

Jaki jest najskuteczniejszy sposób obliczenia wszystkich obecnie przywoływanych i/lub ładowanych zestawów w aplikacji .NET?

Uwaga: Nie interesują mnie metody oparte na plikach, takie jak iterowanie poprzez * .dll w określonym katalogu. Interesuje mnie teraz, czym jest teraz aplikacja przy użyciu numeru.

Odpowiedz

16

Ta metoda rozszerzeń pobiera wszystkie odwołania do złożeń, rekurencyjnie, w tym zagnieżdżonych złożeń.

Podczas korzystania z ReflectionOnlyLoad ładuje zespoły w osobnym AppDomain, co ma tę zaletę, że nie zakłóca procesu JIT.

Zauważysz, że istnieje również MyGetMissingAssembliesRecursive. Możesz użyć tego do wykrycia brakujących złożeń, do których istnieje odwołanie, ale z jakiegoś powodu nie są obecne w bieżącym katalogu. Jest to niezwykle przydatne przy korzystaniu z MEF. Na liście zwrotów pojawi się zarówno brakujący zestaw, jak i jego właściciel (jego rodzic).

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

Aktualizacja

Aby uczynić ten wątek kodu bezpieczne, umieścić lock wokół niego. Domyślnie nie jest to wątek bezpieczny, ponieważ odwołuje się do udostępnionej statycznej zmiennej globalnej, aby zrobić jej magię.

+0

Po prostu przepisałem to, aby było bezpieczne dla wątków, więc można go wywołać z wielu różnych wątków jednocześnie (nie wiem, dlaczego chciałbyś, żeby to było, ale hej, jego bezpieczniej). Daj mi znać, jeśli chcesz, żebym opublikował kod. – Contango

+1

może również opublikować zaktualizowaną wersję. – Michael

+1

@Contango Czy możesz opublikować swoją wersję wątku bezpiecznego, lub jeśli napisałeś o tym blogu, po to? – Robert

176

Pierwsze załadowany zespołów w bieżącym AppDomain:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 

Pierwsze zespoły odwołuje innego zespołu:

var referencedAssemblies = someAssembly.GetReferencedAssemblies(); 

Zauważ, że jeśli jest załadowany montaż referencje Zespół B i montaż A, że robi nie oznacza, że ​​zespół B jest również załadowany. Zespół B zostanie załadowany tylko wtedy, gdy będzie potrzebny. Z tego powodu GetReferencedAssemblies() zwraca instancje AssemblyName zamiast instancji Assembly.

+1

Cóż, potrzebuję czegoś takiego - biorąc pod uwagę rozwiązanie .net Chcę dowiedzieć się wszystkich odwołanych złożeń we wszystkich projektach. Pomysły? –

+0

Należy pamiętać, że obie metody zawierają tylko te biblioteki dll, które są faktycznie używane. Oczywiście nie ma sensu posiadanie referencji w rozwiązaniach, które nie są używane, ale może to być mylące, gdy ktoś próbuje spekulatywnie skanować WSZYSTKIE zgromadzenia. Wszystkie złożenia mogą się nie wyświetlać. – Pompair

+0

@Kumar Vaibhav Dodałem odpowiedź, która właśnie to robi. – Contango

Powiązane problemy