ОБНОВЛЕНИЕ: теперь у меня есть решение, которое меня гораздо больше устраивает. Хотя я и не решил все проблемы, о которых я спрашиваю, оно оставляет путь для этого свободным. Я обновил свой ответ, чтобы отразить это.
Исходный вопрос
Учитывая домен приложения, существует множество различных мест, которые Fusion (загрузчик сборок .Net) будет исследовать для данной сборки. Очевидно, мы принимаем эту функциональность как должное, и поскольку зондирование, по-видимому, встроено в среду выполнения .Net (внутренний метод Assembly._nLoad, кажется, является точкой входа, когда Reflect-Loading), и я предполагаю, что неявная загрузка, вероятно, покрывается тем же базовый алгоритм), поскольку мы, разработчики, похоже, не можем получить доступ к этим путям поиска.
Моя проблема в том, что у меня есть компонент, который выполняет большое количество разрешений динамических типов и который должен быть в состоянии гарантировать, что все развернутые пользователем сборки для данного домена приложения будут предварительно загружены, прежде чем он начнет свою работу. Да, это замедляет запуск, но преимущества, которые мы получаем от этого компонента, полностью перевешивают это.
Базовый алгоритм загрузки, который я уже написал, выглядит следующим образом. Он глубоко сканирует набор папок на предмет наличия любых .dll (.exes исключаются в данный момент) и использует Assembly.LoadFrom для загрузки dll, если ее AssemblyName не может быть найдено в наборе сборок. уже загружены в AppDomain (это реализовано неэффективно, но может быть оптимизировано позже):
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, - это еще одна история, поскольку файловая система виртуализирована), но пару дней назад я столкнулся с интересной проблемой, когда этот код просто не работает : средство запуска тестов nUnit.
При этом используется как теневое копирование (так что моему алгоритму необходимо будет обнаруживать и загружать их из папки сброса теневой копии, а не из папки bin), и он устанавливает PrivateBinPath как относящийся к базовому каталогу.
И, конечно же, есть множество других сценариев хостинга, которые я, вероятно, не рассматривал; но это должно быть действительным, потому что в противном случае Fusion задохнется при загрузке сборок.
Я хочу перестать разбираться и предлагать взлом за взломом, чтобы приспособить эти новые сценарии по мере их появления - я хочу, учитывая домен приложения и его информацию о настройке, возможность создать этот список папок, которые я должен сканировать, чтобы выбрать поднять все библиотеки DLL, которые будут загружены; независимо от того, как настроен домен приложения. Если Fusion видит в них все одинаковые, то и мой код тоже.
Конечно, мне, возможно, придется изменить алгоритм, если .Net изменит свою внутреннюю структуру - это просто крест, который мне придется нести. Точно так же я счастлив рассматривать SQL Server и любые другие подобные среды как крайние случаи, которые пока не поддерживаются.
Любые идеи!?