Как вы просматриваете загруженные в данный момент сборки?
120
У меня есть страница «диагностики» в моем приложении ASP.NET, которая выполняет такие действия, как проверка подключения (-ий) к базе данных, отображение текущих appSettings и ConnectionStrings и т. Д. В разделе этой страницы отображаются версии сборки важных типов, используемых повсюду , но я не мог понять, как эффективно показывать версии ВСЕХ загруженных сборок.
Каков наиболее эффективный способ выяснить все используемые в настоящее время и / или загруженные сборки в приложении .NET?
Примечание. Меня не интересуют файловые методы, такие как итерация по * .dll в определенном каталоге. Мне интересно, что приложение сейчас использует на самом деле .
Этот метод расширения получает все сборки, на которые есть ссылки, рекурсивно, включая вложенные сборки.
По мере использования 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>publicstaticclassGetReferencedAssemblies{staticvoidDemo(){var referencedAssemblies =Assembly.GetEntryAssembly().MyGetReferencedAssembliesRecursive();var missingAssemblies =Assembly.GetEntryAssembly().MyGetMissingAssembliesRecursive();// Can use this within a class.//var referencedAssemblies = this.MyGetReferencedAssembliesRecursive();}publicclassMissingAssembly{publicMissingAssembly(string missingAssemblyName,string missingAssemblyNameParent){MissingAssemblyName= missingAssemblyName;MissingAssemblyNameParent= missingAssemblyNameParent;}publicstringMissingAssemblyName{get;set;}publicstringMissingAssemblyNameParent{get;set;}}privatestaticDictionary<string,Assembly> _dependentAssemblyList;privatestaticList<MissingAssembly> _missingAssemblyList;/// <summary>/// Intent: Get assemblies referenced by entry assembly. Not recursive./// </summary>publicstaticList<string>MyGetReferencedAssembliesFlat(thisType 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>publicstaticDictionary<string,Assembly>MyGetReferencedAssembliesRecursive(thisAssembly assembly){
_dependentAssemblyList =newDictionary<string,Assembly>();
_missingAssemblyList =newList<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>publicstaticList<MissingAssembly>MyGetMissingAssembliesRecursive(thisAssembly assembly){
_dependentAssemblyList =newDictionary<string,Assembly>();
_missingAssemblyList =newList<MissingAssembly>();InternalGetDependentAssembliesRecursive(assembly);return _missingAssemblyList;}/// <summary>/// Intent: Internal recursive class to get all dependent assemblies, and all dependent assemblies of/// dependent assemblies, etc./// </summary>privatestaticvoidInternalGetDependentAssembliesRecursive(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(newMissingAssembly(r.FullName.Split(',')[0], assembly.FullName.MyToName()));}}}}privatestaticstringMyToName(thisstring fullName){return fullName.Split(',')[0];}}
Обновить
Чтобы сделать этот код потокобезопасным, обведите lockего. В настоящее время он не является потокобезопасным по умолчанию, так как он ссылается на общую статическую глобальную переменную, чтобы творить чудеса.
Я просто переписал это, чтобы обеспечить потокобезопасность, чтобы его можно было вызывать одновременно из многих разных потоков (не уверен, зачем вам это нужно, но, эй, это безопаснее). Дайте мне знать, если вы хотите, чтобы я опубликовал код.
Contango
2
@Contango Не могли бы вы опубликовать свою безопасную версию Thread, или, если вы написали об этом в блоге, опубликовать ее?
Роберт
2
Наивный способ сделать этот потокобезопасным - поставить lockвокруг всего этого. Другой метод, который я использовал, устранил зависимость от глобального статического "_dependentAssemblyList", поэтому он стал потокобезопасным без необходимости lock, что дает некоторые небольшие преимущества в скорости, если несколько потоков пытаются одновременно определить, какие сборки отсутствуют (это немного угловой корпус).
Contango,
3
добавление lockне сильно повысит «потокобезопасность». Конечно, это заставляет этот блок кода выполняться только по одному за раз; но другие потоки могут загружать сборки в любое время, когда захотят, и это может вызвать проблемы с некоторыми foreachциклами.
Питер Ричи,
1
@Peter Ritchie Существует общая статическая глобальная переменная, которая используется во время рекурсии, поэтому добавление блокировки вокруг всех обращений к ней сделает эту часть потокобезопасной. Это просто хорошая практика программирования. Обычно все необходимые сборки загружаются при запуске, если не используется что-то вроде MEF, поэтому безопасность потоков на практике не является проблемой.
Contango,
193
Получение загружаемых сборок по текущему AppDomain:
var loadedAssemblies =AppDomain.CurrentDomain.GetAssemblies();
Получение сборок, на которые ссылается другая сборка:
var referencedAssemblies = someAssembly.GetReferencedAssemblies();
Обратите внимание: если сборка A ссылается на сборку B и сборка A загружена, это не означает, что сборка B также загружена. Сборка B будет загружена только тогда и тогда, когда это необходимо. По этой причине GetReferencedAssemblies()возвращает AssemblyNameэкземпляры, а не Assemblyэкземпляры.
Мне нужно что-то вроде этого - учитывая решение .net, я хочу узнать все сборки, на которые есть ссылки, во всех проектах. Идеи?
Кумар Вайбхав 09
Обратите внимание, что оба метода перечисляют только фактически используемые библиотеки DLL. Очевидно, нет смысла иметь ссылки на решения, которые не используются, но это может сбивать с толку, когда кто-то пытается спекулятивно сканировать ВСЕ сборки. Все сборки могут просто не отображаться.
Pompair
3
OP запрашивает загруженные в данный момент сборки, на которые нет ссылок. Это отвечает на вопрос. Именно то, что я искал.
lock
вокруг всего этого. Другой метод, который я использовал, устранил зависимость от глобального статического "_dependentAssemblyList", поэтому он стал потокобезопасным без необходимостиlock
, что дает некоторые небольшие преимущества в скорости, если несколько потоков пытаются одновременно определить, какие сборки отсутствуют (это немного угловой корпус).lock
не сильно повысит «потокобезопасность». Конечно, это заставляет этот блок кода выполняться только по одному за раз; но другие потоки могут загружать сборки в любое время, когда захотят, и это может вызвать проблемы с некоторымиforeach
циклами.Получение загружаемых сборок по текущему
AppDomain
:Получение сборок, на которые ссылается другая сборка:
Обратите внимание: если сборка A ссылается на сборку B и сборка A загружена, это не означает, что сборка B также загружена. Сборка B будет загружена только тогда и тогда, когда это необходимо. По этой причине
GetReferencedAssemblies()
возвращаетAssemblyName
экземпляры, а неAssembly
экземпляры.источник