Autofac failing to Resolve Module when module instantiated with Activator

275 views Asked by At

I am attempting to use a custom attribute to define modules in external libraries I want my framework container to load. I’m able to scan the assembly, find and validate my types, and return a list of instantiated IModules. However when I attempt to resolve a Type that was registered in the external module the type fails to resolved.

Main library targeting .Net Standard 2.0

public static List<IModule> DiscoverContainerModules()
{
   var modules = new List<IModule>();
   var assemblies = DiscoverAssemblies();

   foreach (var assembly in assemblies)
    {
      modules.AddRange(from type in assembly.GetTypes() 
                    where type.GetCustomAttribute<AppkitModuleAttribute>() != null 
                    where type.IsAssignableTo<IModule>() 
                    select Activator.CreateInstance(type) into module 
                    select module as IModule);
            }
            return modules;
 }

Extension method to register modules

public static void UseAppkitModules(this ContainerBuilder builder)
 {
   var modules = AppkitPluginDiscovery.DiscoverContainerModules();

   foreach (var module in modules)
    {
      builder.RegisterModule(module);
    }
 }

From the hosted application targeting .Net Core 6.0

builder.UseAppkitModules();

And finally an example of a module from another .Net Standard 2.0 library

[AppkitModule(nameof(DisplayModule))]
public class DisplayModule : Module
{
  protected override void Load(ContainerBuilder builder)
   {
    builder.Register(c =>
     {
      var conductor = c.Resolve<IConductor>();
      var logger = c.Resolve<ILogger>();

       return new DisplaySubsystem(
          conductor: conductor,
          logger: logger);
       });
     }
 }

The module works fine if I simply new it up builder.RegisterModule(new DisplayModule()); And I’ve confirmed the Load method is called using my reflection methods. Why is Creating the module instance with Activator behaving differently. I’ve also confirm the assembly is loaded.

I’ve moved the module into the parent library and it fails as well. I’ve broken down all the extension methods into a large code block and get the same results. I’ve even create the instance with Activator and got the same results.

1

There are 1 answers

0
Eric Williams On

Solved:

The root of the issues was my method to load and scan the assemblies with my custom attribute applied. I was using Assembly.LoadFile(path). It would seem that didn't provide the required dependancies to load my module. I updated the method to use Assembly.LoadFrom(path) and all modules and types resolve perfectly.

Full Solution - Load Assemblies

private static List<Assembly> LoadReferencedAssemblies()
{
    var assemblies = new List<Assembly>();

    var path = AppContext.BaseDirectory;
    var directory = new DirectoryInfo(path);

    if (!directory.Exists) return assemblies;

    var libraries = directory.GetFiles("*.dll");

    assemblies.AddRange(libraries.Select(fileInfo => Assembly.LoadFrom(fileInfo.FullName)));

    return assemblies;
}

Get Assemblies with Custom Attributes

private static List<Assembly> DiscoverAssemblies()
{
    var assemblies = new List<Assembly>();
    var attributes = DiscoverAttributes();
    var domain = LoadReferencedAssemblies();

    foreach (var assembly in domain)
    {
        assemblies.AddRange(
            from type in assembly.GetTypes()
            where attributes.Any(attribute => type.GetCustomAttribute(attribute) != null)
            where !assemblies.Contains(assembly)
            select assembly);
    }
    return assemblies;
}

Get All Types with Module Attribute

public static List<AppkitAssemblyType> DiscoverModules()
{
    var modules = new List<AppkitAssemblyType>();

    modules.AddRange(
        from assembly in AppkitAssemblies
        from type in assembly.GetTypes()
        where type.GetCustomAttribute<AppkitModuleAttribute>() != null
        select new AppkitAssemblyType(type));

    return modules;
}

Builder Extension


public static ContainerBuilder RegisterAppkitModules(this ContainerBuilder builder)
{
     var modules = AppkitPluginDiscovery.DiscoverModules();

      foreach (var module in modules)
      {
           builder.RegisterAssemblyModules(module.Type, module.Assembly);
      }

      return builder;
}

Example Module in Referenced Assembly


[AppkitModule(nameof(DisplayModule))]
public class DisplayModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<DisplaySubsystem>().AsSelf().SingleInstance();
    }
}

Finally Container Configuration

builder.RegisterAppkitModules()