Is there a way to force all referenced assemblies to be loaded into the app domain?

My projects are set up like this:

  • Project "Definition"
  • Project "Implementation"
  • Project "Consumer"

Project "Consumer" references both "Definition" and "Implementation", but does not statically reference any types in "Implementation".

When the application starts, Project "Consumer" calls a static method in "Definition", which needs to find types in "Implementation"

Is there a way I can force any referenced assembly to be loaded into the App Domain without knowing the path or name, and preferably without having to use a full-fledged IOC framework?


Solution 1:

This seemed to do the trick:

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))));

As Jon noted, the ideal solution would need to recurse into the dependencies for each of the loaded assemblies, but in my specific scenario I don't have to worry about it.


Update: The Managed Extensibility Framework (System.ComponentModel) included in .NET 4 has much better facilities for accomplishing things like this.

Solution 2:

You can use Assembly.GetReferencedAssemblies to get an AssemblyName[], and then call Assembly.Load(AssemblyName) on each of them. You'll need to recurse, of course - but preferably keeping track of assemblies you've already loaded :)

Solution 3:

just wanted to share a recursive example. I'm calling the LoadReferencedAssembly method in my startup routine like this:

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

This is the recursive method:

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));
        }
    }
}

Solution 4:

If you use Fody.Costura, or any other assembly merging solution, the accepted answer will not work.

The following loads the Referenced Assemblies of any currently loaded Assembly. Recursion is left to you.

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)));

Solution 5:

Seeing as I had to load an assembly + dependencies from a specific path today I wrote this class to do it.

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);
    }
}