ОБНОВЛЕНИЕ: У меня теперь есть решение, которое я намного счастливее с этим, хотя и не решая все проблемы, о которых я прошу, это оставляет ясным понять это. Я обновил свой собственный ответ, чтобы отразить это.
Оригинальный вопрос
Учитывая домен приложения, существует множество разных мест, которые Fusion (загрузчик сборок .Net) будет запрашивать для данной сборки. Очевидно, что мы используем эту функцию как должное и, поскольку зондирование, кажется, внедрено в среду выполнения .Net(внутренний метод Assembly._nLoad
, кажется, является точкой входа, когда Reflect-Loading), и я предполагаю, что неявная загрузка, вероятно, покрывается один и тот же базовый алгоритм), поскольку разработчики, похоже, не могут получить доступ к этим путям поиска.
Моя проблема в том, что у меня есть компонент, который выполняет много динамического разрешения, и который должен быть в состоянии обеспечить, чтобы все пользовательские сборки для данного AppDomain были предварительно загружены, прежде чем он начнет свою работу. Да, это замедляет запуск, но преимущества, которые мы получаем от этого компонента, полностью обходятся этим.
Основной алгоритм загрузки, который я уже написал, выглядит следующим образом. Он глубоко сканирует набор папок для любой DLL (в настоящее время исключаются исключения .exe) и использует 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 Server-based AppDomains - это еще одна история с момента виртуализации файловой системы), но пару дней назад я столкнулся с интересной проблемой, когда этот код просто не работает 't work: nNnit test runner.
В этом случае используется как теневое копирование (поэтому мой алгоритм должен будет обнаруживать и загружать их из папки drop-copy drop, а не из папки bin), и он устанавливает PrivateBinPath как относящийся к базовому каталогу.
И, конечно, есть множество других сценариев хостинга, которые я, вероятно, не рассматривал; но который должен быть действительным, потому что иначе Fusion задохнется при загрузке сборок.
Я хочу перестать чувствовать себя вокруг и внедрить хак для взлома, чтобы приспособиться к этим новым сценариям, поскольку они возникают - я хочу, учитывая AppDomain и его информацию о настройке, возможность создания этого списка папок, которые я должен сканировать в заказать все DLL файлы, которые будут загружены; независимо от того, как настроен AppDomain. Если Fusion увидит их как все одинаковые, тогда мой код тоже.
Конечно, мне, возможно, придется изменить алгоритм, если .Net изменит его внутренности - это просто крест, который мне придется нести. В равной степени я рад рассмотреть SQL Server и любые другие подобные среды, такие как крайние регистры, которые пока остаются неподдерживаемыми.
Любые идеи!?