【问题标题】:How do you loop through currently loaded assemblies?你如何循环当前加载的程序集?
【发布时间】:2010-09-27 21:00:06
【问题描述】:

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

找出 .NET 应用程序中所有当前引用和/或加载的程序集的最有效方法是什么?

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

【问题讨论】:

    标签: .net assemblies


    【解决方案1】:

    获取当前AppDomain的加载程序集:

    var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
    

    获取另一个程序集引用的程序集:

    var referencedAssemblies = someAssembly.GetReferencedAssemblies();
    

    请注意,如果程序集 A 引用程序集 B 并且程序集 A 已加载,这并不意味着程序集 B 也已加载。仅在需要时才加载程序集 B。因此,GetReferencedAssemblies() 返回 AssemblyName 实例而不是 Assembly 实例。

    【讨论】:

    • 嗯,我需要这样的东西 - 给定一个 .net 解决方案,我想找出所有项目中所有引用的程序集。想法?
    • 请注意,这两种方法都只列出实际使用的dll。显然,在未使用的解决方案中引用是没有意义的,但是当有人试图推测性地扫描所有程序集时,这可能会令人困惑。所有程序集可能都不会显示。
    • OP 询问 当前加载的 程序集而不是引用的程序集。这回答了这个问题。正是我想要的。
    • 知道何时加载程序集 B 的事件?
    【解决方案2】:

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

    由于它使用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];
        }
    }
    

    更新

    为了使这个代码线程安全,在它周围加上一个lock。默认情况下它目前不是线程安全的,因为它引用了一个共享的静态全局变量来发挥它的魔力。

    【讨论】:

    • 我只是将其重写为线程安全的,因此可以同时从许多不同的线程调用它(不知道你为什么想要这样,但是嘿,它更安全)。如果您希望我发布代码,请告诉我。
    • @Contango 您能否发布您的 Thread 安全版本,或者如果您写了一篇关于它的博客,请发布?
    • 让这个线程安全的天真的方法是在整个事情周围放置一个lock。我使用的另一种方法消除了对全局静态“_dependentAssemblyList”的依赖,因此它变得线程安全而无需lock,如果多个线程试图同时确定缺少哪些程序集(这是有点极端)。
    • 添加 lock 不会增加“线程安全”的方式。当然,这使得该代码块一次只执行一个;但其他线程可以随时加载程序集,这可能会导致某些foreach 循环出现问题。
    • @Peter Ritchie 在递归期间使用了一个共享静态全局变量,因此在所有访问权限周围添加一个锁将使该部分线程安全。这只是很好的编程习惯。通常所有必需的程序集都会在启动时加载,除非使用了像 MEF 这样的东西,所以线程安全在实践中并不是真正的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多