Autofac: How to load assemblies that are referenced but not directly used

11,242

Solution 1

This should help you. It takes all the assemblies in the bin folder, starting with name MyModule.

   //builder.RegisterType(typeof(IFoo)).AsImplementedInterfaces();
        ContainerBuilder builder = new ContainerBuilder();

        string[] assemblyScanerPattern = new[] { @"MyModule.*.dll"};

        // Make sure process paths are sane...
        Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

        //  begin setup of autofac >>

        // 1. Scan for assemblies containing autofac modules in the bin folder
        List<Assembly> assemblies = new List<Assembly>();
        assemblies.AddRange(
            Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.dll", SearchOption.AllDirectories)
                     .Where(filename => assemblyScanerPattern.Any(pattern => Regex.IsMatch(filename, pattern)))
                     .Select(Assembly.LoadFrom)
            );


        foreach (var assembly in assemblies)
        {
            builder.RegisterAssemblyTypes(assembly )
                .AsImplementedInterfaces();
        }
        var container = builder.Build();

This will load those assemblies also which are not even referenced.

Solution 2

var assemblies = Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.TopDirectoryOnly)
   .Where(filePath => Path.GetFileName(filePath).StartsWith("your name space"))
   .Select(Assembly.LoadFrom);

var builder = new ContainerBuilder();

builder.RegisterAssemblyTypes(assemblies.ToArray())
   .Where(t => typeof(ITransientDependency).IsAssignableFrom(t))
   .AsImplementedInterfaces();

Solution 3

//Short answer

Have you seen this: http://docs.autofac.org/en/latest/faq/iis-restart.html It looks like you can scan the referenced assemblies and force them to load.

//Suggestion (or opinion if you like)

Personally I wouldn't have assemblies loaded that weren't going to be used just to be able to have a separate project for the bootstrapping. I don't see the benefit of having it in it's own project. Keep in mind that DI is just that, you are injecting your dependencies of "that" project. Each project can have it's own dependencies and the separation of concern comes from the bootstrapping class, not that it's in it's own project. If you needed to break out just your business logic project into a nuget package that other projects in your company would use you would not want them to also have to download the DI project package that in turn now needs other packages that it may never use. If your dependencies are contained in the project that needed them it becomes modular and extensible. This part of the answer is debatable of course but should be considered at the least.

Solution 4

In case you have multiple projects in your solution, each with their own IoC container class, use the following to load all assemblies, and then register each module in that Assembly.

This is an extension method of the ContainerBuilder.

public static class ContainerBuilderExtensions
{
    public static void ScanAssembly(this ContainerBuilder builder, string searchPattern = "SolutionName.*.dll")
    {
        var path = AppDomain.CurrentDomain.BaseDirectory;
        foreach (var assembly in Directory.GetFiles(path, searchPattern).Select(Assembly.LoadFrom))
        {
            builder.RegisterAssemblyModules(assembly);
        }
    }
}

The above can be called on an instance of your ContainerBuilder as follow:

builder.ScanAssembly()
Share:
11,242
Francisco d'Anconia
Author by

Francisco d'Anconia

Long time dev working in .Net stack. You can read more about me here: http://www.cliffsnotes.com/literature/a/atlas-shrugged/character-analysis/francisco-danconia

Updated on June 24, 2022

Comments

  • Francisco d'Anconia
    Francisco d'Anconia almost 2 years

    We have created a WebApi solution using Autofac for DI. We broke out the bootstrapping of our autofac into a separate project. This way, our WebApi project only references our Bootstrap and Contracts projects. Our bootstrap project then references all other assemblies and wires everything together. I like this design for separation of concerns.

    We can manually load our assemblies as follows - where our "AutofacModule" classes contain the necessary info to register each module (assembly).

    ContainerBuilder builder = new Autofac.ContainerBuilder();
    builder.RegisterModule(new Business.AutofacModule());
    builder.RegisterModule(new Data.AutofacModule());
    builder.RegisterModule(new Services.AutofacModule());
    etc...
    

    This works, but requires hardcoding each assembly. We are trying to make this dynamic so that we can just loop over all referenced assemblies as follows.

    var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
    foreach (var assembly in assemblies)
    {
        builder.RegisterAssemblyModules(assembly);
    }
    

    This should work but doesn't. The problem is that .Net determines various assemblies aren't actually used from within the bootstrap project and doesn't load them (in an attempt to optimize?). So some of our assemblies are never loaded.

    I have also tried the following to loop through the bin directory to find all assemblies. However, during compilation, .Net doesn't move the non-referenced assemblies into the bin directory, so they aren't there either.

    string assemblyPath = System.IO.Path.Combine(
        System.AppDomain.CurrentDomain.BaseDirectory, "bin");
    var allAssemblies = new List<Assembly>();
    foreach (string dll in Directory.GetFiles(assemblyPath, "*.dll"))
    {
        allAssemblies.Add(Assembly.LoadFile(dll));
    }
    

    I have set the assemblies to Copy Local which didn't work. I read about a Copy Local bug and tried workarounds for that which didn't work either.

    Has anyone been able to solve this issue? It seems like Autofac would provide a solution, but all I found was a To Do page on their documents: http://autofac.readthedocs.org/en/latest/faq/isolate-autofac.html

    The following two questions are similar, but none of the proposed solutions overcome the fact that the needed assemblies are not in the bin directory.

    Not all assemblies are being loaded into AppDomain from the bin folder

    Loading all referenced assemblies .NET even if not used explicitly in code

    Finally, I'm curious, is this an Autofac specific problem? How do other DI containers solve this problem? I found a similar problem for NInject. Loading unreferenced dll MVC Ninject

  • hungryMind
    hungryMind about 7 years
    builder.RegisterAssemblyTypes accepts array of assemblies, for each is not required