ОБНОВЛЕНИЕ: Теперь у меня есть решение, которым я намного доволен, хотя и не решает всех проблем, о которых я спрашиваю, это делает путь ясным. Я обновил свой собственный ответ, чтобы отразить это.
Оригинальный вопрос
Для данного домена приложений существует много разных мест, которые Fusion (загрузчик сборок .Net) будет проверять на наличие данной сборки. Очевидно, что мы принимаем эту функциональность как должное и, поскольку зондирование, кажется, встроено в среду выполнения .Net (внутренний метод Assembly._nLoad
кажется отправной точкой при отраженной загрузке - и я предполагаю, что неявная загрузка, вероятно, покрывается тот же базовый алгоритм), что, как разработчики, мы не можем получить доступ к этим путям поиска.
Моя проблема в том, что у меня есть компонент, который выполняет динамическое разрешение типов и должен иметь возможность гарантировать, что все развернутые пользователем сборки для данного AppDomain предварительно загружены, прежде чем он начнет свою работу. Да, это замедляет запуск - но преимущества, которые мы получаем от этого компонента, полностью перевешивают это.
Основной алгоритм загрузки, который я уже написал, выглядит следующим образом. Он глубоко сканирует набор папок для любых .dll (исключая .exes на данный момент ) и использует Assembly.LoadFrom для загрузки dll, если его AssemblyName уже не может быть найдено в наборе сборок. загружен в домен приложений (это реализовано неэффективно, но его можно оптимизировать позже):
void PreLoad(IEnumerable<string> paths)
{
foreach(path p in paths)
{
PreLoad(p);
}
}
void PreLoad(string p)
{
//all try/catch blocks are elided for brevity
string[] files = null;
files = Directory.GetFiles(p, "*.dll", SearchOption.AllDirectories);
AssemblyName a = null;
foreach (var s in files)
{
a = AssemblyName.GetAssemblyName(s);
if (!AppDomain.CurrentDomain.GetAssemblies().Any(
assembly => AssemblyName.ReferenceMatchesDefinition(
assembly.GetName(), a)))
Assembly.LoadFrom(s);
}
}
LoadFrom используется, потому что я обнаружил, что использование Load () может привести к тому, что Fusion будет загружать дублирующиеся сборки, если при поиске он не найдет загруженную там, где он ожидает его найти.
Итак, с этим на месте все, что мне теперь нужно сделать, - это получить список в порядке приоритета (от наивысшего к низшему) путей поиска, которые Fusion будет использовать при поиске сборки. Тогда я могу просто перебрать их.
GAC для этого не имеет значения, и меня не интересуют какие-либо фиксированные пути, управляемые средой, которые Fusion может использовать - только те пути, которые можно найти из AppDomain, которые содержат сборки, явно развернутые для приложения.
Моя первая итерация этого просто использованного AppDomain.BaseDirectory. Это работает для сервисов, приложений форм и консольных приложений.
Это не работает для веб-сайта Asp.Net, однако, поскольку есть как минимум два основных местоположения - AppDomain.DynamicDirectory (где Asp.Net размещает динамически генерируемые классы страниц и любые сборки, на которые ссылается код страницы Aspx. ), а затем папку Bin сайта, которую можно найти в свойстве AppDomain.SetupInformation.PrivateBinPath.
Итак, теперь у меня есть рабочий код для самых основных типов приложений (домены приложений на Sql Server - еще одна история, поскольку файловая система виртуализирована) - но я столкнулся с интересной проблемой пару дней назад, когда этот код просто не не работает: тестовый блок nUnit.
При этом используется как теневое копирование (поэтому мой алгоритм должен был бы обнаруживать и загружать их из папки отбрасывания теневого копирования, а не из папки bin), так и для PrivateBinPath он задается относительно базового каталога.
И, конечно, есть множество других сценариев хостинга, которые я, вероятно, не рассматривал; но это должно быть допустимо, потому что в противном случае Fusion будет задыхаться при загрузке сборок.
Я хочу перестать чувствовать себя вокруг и вводить хак на хак, чтобы приспособиться к этим новым сценариям по мере их появления - что я хочу, так это, учитывая AppDomain и информацию о его настройке, возможность создавать этот список папок, в которых я должен сканировать для того, чтобы забрать все библиотеки DLL, которые будут загружены; независимо от того, как настроен AppDomain. Если Fusion видит их одинаковыми, то и мой код должен быть таким же.
Конечно, мне, возможно, придется изменить алгоритм, если .Net изменит свои внутренние компоненты - это просто крест, который я должен вынести. Точно так же я рад рассматривать SQL Server и любые другие подобные среды как крайние случаи, которые пока не поддерживаются.
Есть идеи!?