我的ASP.NET应用程序中有一个"诊断"页,它执行诸如验证数据库连接、显示当前appSettings和ConnectionString等操作.此页的一部分显示了贯穿始终的重要类型的程序集版本,但我不知道如何有效地显示所有已加载程序集的版本.

What is the most effective way to figure out all currently referenced and/or loaded Assemblies in a .NET application?

注意:我对基于文件的方法不感兴趣,比如迭代特定目录中的*.dll.我感兴趣的是应用程序现在实际是using.

推荐答案

此扩展方法递归地获取所有引用的程序集,包括嵌套的程序集.

因为它使用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.默认情况下,它目前不是线程安全的,因为它引用了一个共享的静态全局变量来发挥作用.

.net相关问答推荐

无法在Designer、VS2022、. NET 8中打开WinForms表单'

EF核心类似功能T-SQL UPDATE FROM

如何将多个安装程序Bundle 到一个安装程序中?

是否有内置方法将 nuget 包引用为 csproj 中的文件?

为什么我在环境变量中有不同的值?

.NET MAUI 的登录页面

out 和 ref 可以用作临时变量吗?

如何正确使用await using语法?

无法解析此引用.找不到程序集

.NET 世界是否有 Maven 替代方案或端口?

HttpClient 和使用代理 - 不断得到 407

无法加载文件或程序集WebGrease,版本=1.5.1.25624,Culture=neutral,PublicKeyToken=31bf3856ad364e35或其依赖项之一

C# 中 try/finally 的开销?

如何从字符串中删除所有字母字符?

如何在 WebBrowser 控件中注入 Javascript?

.NET 中的对象引用有多大?

.NET 配置文件 configSource 在应用程序目录文件夹之外

在 C# 中使用 Bitmap 对象查找图像格式

泛型类的静态成员是否与特定实例相关联?

捕获控制台退出 C#