我在ASP.NET应用程序中有一个“诊断”页面,该页面执行诸如验证数据库连接,显示当前appSettings和ConnectionStrings等操作。此页面的一部分显示了在整个过程中使用的重要类型的Assembly版本,但我不知道如何有效显示所有已加载程序集的版本。
找出.NET应用程序中所有当前引用和/或加载的程序集的最有效方法是什么?
注意:我对基于文件的方法不感兴趣,例如遍历特定目录中的* .dll。我对应用程序当前实际使用的内容感兴趣。
我在ASP.NET应用程序中有一个“诊断”页面,该页面执行诸如验证数据库连接,显示当前appSettings和ConnectionStrings等操作。此页面的一部分显示了在整个过程中使用的重要类型的Assembly版本,但我不知道如何有效显示所有已加载程序集的版本。
找出.NET应用程序中所有当前引用和/或加载的程序集的最有效方法是什么?
注意:我对基于文件的方法不感兴趣,例如遍历特定目录中的* .dll。我对应用程序当前实际使用的内容感兴趣。
Answers:
此扩展方法以递归方式获取所有引用的程序集,包括嵌套程序集。
使用时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
周围放一个。默认情况下,它当前不是线程安全的,因为它引用共享的静态全局变量来发挥作用。
lock
。我使用的另一种方法消除了对全局静态“ _dependentAssemblyList”的依赖,因此不需要a即可成为线程安全的lock
,如果多个线程试图同时确定缺少哪些程序集,则它具有一些速度上的优势(这有点角落案例)。
lock
不会以“线程安全”的方式增加太多。当然,这使得该代码块一次只能执行一次;但是其他线程可以在需要时随时加载程序集,这可能会导致某些foreach
循环出现问题。
获取当前的装配件AppDomain
:
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
获取另一个程序集引用的程序集:
var referencedAssemblies = someAssembly.GetReferencedAssemblies();
请注意,如果程序集A引用了程序集B,而程序集A已加载,则并不意味着程序集B也已加载。仅在需要时以及在需要时才加载程序集B。因此,GetReferencedAssemblies()
返回AssemblyName
实例而不是Assembly
实例。