Как вы прокручиваете загруженные в настоящее время сборки?

У меня есть страница "диагностики" в моем приложении ASP.NET, которая делает такие вещи, как проверка соединений с базой данных, отображение текущих настроек приложения и ConnectionStrings и т.д. В разделе этой страницы отображаются версии Ассамблеи важных типы, используемые повсюду, но я не мог понять, как эффективно показывать версии ВСЕ загруженных сборок.

Каков наиболее эффективный способ определения всех в настоящее время ссылок и/или загруженных ассемблий в приложении .NET?

Примечание. Мне не нужны методы на основе файлов, такие как итерация через *.dll в определенном каталоге. Меня интересует, что приложение действительно использует прямо сейчас.

Ответ 1

Этот метод расширений рекурсивно получает все ссылочные сборки, включая вложенные сборки.

Поскольку он использует ReflectionOnlyLoad, он загружает сборки в отдельный AppDomain, который имеет то преимущество, что не мешает процессу JIT.

Вы заметите, что есть также MyGetMissingAssembliesRecursive. Вы можете использовать это для обнаружения любых отсутствующих сборок, на которые ссылаются, но по какой-либо причине не присутствуют в текущем каталоге. Это невероятно полезно при использовании MEF. Возвратный список предоставит вам как недостающую сборку, так и ее владельцу (ее родительскому элементу).

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

Update

Чтобы сделать этот поток кода безопасным, поместите вокруг него lock. По умолчанию он не является потокобезопасным, поскольку он ссылается на общую статическую глобальную переменную, чтобы сделать свою магию.

Ответ 2

Получение загруженных сборок для текущего AppDomain:

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

Получение сборок, на которые ссылается другая сборка:

var referencedAssemblies = someAssembly.GetReferencedAssemblies();

Обратите внимание, что если сборка A ссылается на сборку B и сборку A, это не означает, что сборка B также загружена. Сборка B будет загружаться только тогда, когда это необходимо. По этой причине GetReferencedAssemblies() возвращает экземпляры AssemblyName, а не Assembly экземпляры.