Есть ли способ заставить все ссылочные сборки загружаться в домен приложения?

Мои проекты настроены следующим образом:

  • Проект "Определение"
  • Проект "Внедрение"
  • Проект "Потребитель"

Проект "Потребитель" ссылается как на "Определение", так и "Реализация" , но не статически ссылается на любые типы в "Реализация" .

Когда приложение запускается, Project "Consumer" вызывает статический метод в "Определении", который должен найти типы в "Реализация"

Есть ли способ заставить любую ссылочную сборку загружаться в домен приложения без знания пути или имени и, желательно, без использования полноценной структуры IOC?

Ответ 1

Казалось, это трюк:

        var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
        var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();

        var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
        var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
        toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));

Как отметил Джон, идеальное решение необходимо будет учитывать в зависимостях для каждой из загруженных сборок, но в моем конкретном сценарии мне не нужно беспокоиться об этом.


Обновление: Рамка Managed Extensibility Framework (System.ComponentModel), включенная в .NET 4, имеет гораздо лучшие возможности для выполнения таких задач.

Ответ 2

Вы можете использовать Assembly.GetReferencedAssemblies, чтобы получить AssemblyName[], а затем вызвать Assembly.Load(AssemblyName) для каждого из них. Разумеется, вам нужно будет записаться, но желательно следить за сборками, которые вы уже загрузили:)

Ответ 3

просто хотел поделиться рекурсивным примером. Я вызываю метод LoadReferencedAssembly в моей программе запуска следующим образом:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    this.LoadReferencedAssembly(assembly);
}

Это рекурсивный метод:

private void LoadReferencedAssembly(Assembly assembly)
{
    foreach (AssemblyName name in assembly.GetReferencedAssemblies())
    {
        if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName))
        {
            this.LoadReferencedAssembly(Assembly.Load(name));
        }
    }
}

Ответ 4

Если вы используете Fody.Costura или любое другое решение для слияния сборки, принятый ответ не будет работать.

Следующая загрузка ссылочных сборок любой загруженной в данный момент сборки. Рекурсия оставлена ​​вам.

var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

loadedAssemblies
    .SelectMany(x => x.GetReferencedAssemblies())
    .Distinct()
    .Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false)
    .ToList()
    .ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));

Ответ 5

Видя, как мне приходилось загружать сборки + зависимости из определенного пути, я написал этот класс для этого.

public static class AssemblyLoader
{
    private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>();

    static AssemblyLoader()
    {
        AssemblyDirectories[GetExecutingAssemblyDirectory()] = true;
        AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;

    }

    public static Assembly LoadWithDependencies(string assemblyPath)
    {
        AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true;
        return Assembly.LoadFile(assemblyPath);
    }

    private static Assembly ResolveAssembly(object sender, ResolveEventArgs args)
    {
        string dependentAssemblyName = args.Name.Split(',')[0] + ".dll";
        List<string> directoriesToScan = AssemblyDirectories.Keys.ToList();

        foreach (string directoryToScan in directoriesToScan)
        {
            string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName);
            if (File.Exists(dependentAssemblyPath))
                return LoadWithDependencies(dependentAssemblyPath);
        }
        return null;
    }

    private static string GetExecutingAssemblyDirectory()
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        var uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}