Как вы перебираете загруженные в данный момент сборки? - PullRequest
117 голосов
/ 20 декабря 2008

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

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

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

Ответы [ 2 ]

189 голосов
/ 20 декабря 2008

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

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

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

var referencedAssemblies = someAssembly.GetReferencedAssemblies();

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

22 голосов
/ 10 октября 2014

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

Поскольку он использует ReflectionOnlyLoad, он загружает сборки в отдельном домене приложений, что дает преимущество в том, что он не мешает процессу 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];
    }
}

Обновление

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...