I'm using Autofac as a dependency injection system in a C# solution that spans several class libraries and several executables. I'm using modules to configure Autofac, but that still leaves me with the issue of building the DI container, which varies depending upon which executable I'm composing it for.
I tried using Autofac's RegisterAssemblyModules, but you have to give it a list of assemblies to scan, and until some Type in a class library assembly is used, the assembly isn't loaded, and hence not available to scan.
Some people recommend loading every assembly in the bin directory which might have an Autofac module definition in it. But that seems to pose the risk of an undesired assembly getting slipped into action.
So what I came up with is this static class, which is defined in a common class library:
public static class Container
{
private static IContainer _instance;
private static Dictionary<string, Assembly> _moduleAssemblies = new Dictionary<string, Assembly>();
public static void RegisterAutofacModuleAssembly<T>()
where T : class
{
var assembly = typeof(T).Assembly;
if( !_moduleAssemblies.ContainsKey( assembly.FullName ) )
_moduleAssemblies.Add( assembly.FullName, assembly );
}
public static IContainer Instance
{
get
{
if( _instance == null )
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyModules( _moduleAssemblies.Select( ma => ma.Value ).ToArray() );
_instance = builder.Build();
}
return _instance;
}
}
}
You use this by including lines like this in the startup code of an application:
public static void Main(string[] args)
{
AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<ScannerApp>();
AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<AppConfiguration>();
Is this a reasonable solution? If there's a better, more flexible one I'd be interested in learning about it.
Issue in IOptions<>
Binding System
In implementing @Nightowl888's suggestion, I ran into a problem with the Microsoft configuration IOptions<>
system. Here's how I'm trying to configure Autofac to resolve AppConfiguration objects:
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var config = new ConfigurationBuilder()
.AddJsonFile( AppConfiguration.WebJobsConfigFile, false )
.AddUserSecrets<ConfigurationModule>()
.AddEnvironmentVariables()
.Build();
builder.Register<AppConfiguration>( ( c, p ) =>
{
var retVal = new AppConfiguration( c.Resolve<ILogger>() );
config.Bind( retVal );
return retVal;
} )
.SingleInstance();
}
The problem occurs in the call to Bind(). As it traverses and parses the configuration information, it expects to create various objects thru parameterless constructors...which makes it difficult to use constructor injectcion.
If I can't use constructor injection, I need to be able to resolve against the DI container within constructor code. I don't see how I can define a library assembly which doesn't hardwire in a particular DI container's resolution semantics.
Thoughts? Other than "abandon the IOptions<>
system", which I've considered, but it provides a number of benefits I'd like to maintain.