A dependency module (derived from Autofac.Module) that discovers and registers all other dependency modules (derived from Autofac.Module).
The dependency modules can also have shared dependencies injected into them, e.g. IConfiguration, IHostEnvironment and even IServiceCollection.
- Introduction
- Releases
- Autofac version
- Summary
- Assembly registry
- Option #1 - Use the default assembly registry
- Option #2 - Use the pre-initialized default assembly registry
- Option #3 - Use your custom assembly registry (interface + parameterless ctor)
- Option #4 - Use your custom assembly registry (interface only)
- Option #5 - Use your custom assembly registry (it will be used through reflection)
AutofacServiceProviderFactoryDecoratorContainerBuilderextension methods- Examples
The motivations behind this project are:
- to automatically discover and register all dependency modules in projects that are referenced by the startup project
- to be able to access
IConfiguration,IHostEnvironmentinstances from within the dependency modules - to be able to use
IServiceCollectionextension methods in them.
You can download the package from nuget.org.
You can find the release notes here.
| EgonsoftHU.Extensions.DependencyInjection.Autofac | 1.0.0 - 2.0.0 | 3.0.0 | 4.0.0 |
|---|---|---|---|
| Autofac.Extensions.DependencyInjection dependency type: |
- | 8.0.0 direct / top-level |
10.0.0 direct / top-level |
| Autofac dependency type: |
4.9.4 direct / top-level |
6.4.0 indirect / transitive |
8.1.0 indirect / transitive |
| Target frameworks | netstandard2.0netstandard2.1net461netcoreapp3.1net6.0 |
netstandard2.0netstandard2.1net472netcoreapp3.1net6.0 |
netstandard2.0netstandard2.1net472net6.0net8.0 |
Note: .NET Framework target version changed because Autofac.WebApi2 that can be used with Autofac 6+ is targeted only to .NET Framework 4.7.2.
This solution uses an assembly registry, a decorator class for AutofacServiceProviderFactory and an Autofac module.
The assembly registry contains all the relevant assemblies that may have a dependency module.
The decorator class (EgonsoftHU.Extensions.DependencyInjection.AutofacServiceProviderFactoryDecorator)
- captures the existing
IServiceCollectioninstance
when theIServiceProviderFactory<ContainerBuilder>.CreateBuilder(IServiceCollection)method is called and - shares a copy of it to the
Autofacmodule below.
The Autofac module (EgonsoftHU.Extensions.DependencyInjection.DependencyModule)
- checks all the assemblies in the assembly registry for types derived from
Autofac.Moduletype - into a temporary
ContainerBuilderinstance- registers them, hence you will be able to inject dependencies into your dependency modules, e.g.
IConfiguration - registers other dependencies you specified, e.g.
IConfiguration - registers a copy of the shared
IServiceCollectioninstance
(except if you already specified one, but then you need to handle possible service registration overrides)
- registers them, hence you will be able to inject dependencies into your dependency modules, e.g.
- builds the temporary
IContainerinstance - resolves the dependency modules from it
- registers them into the actual
ContainerBuilderinstance - finally, the service registrations that are newly added to the shared
IServiceCollectioninstance are populated into the actualContainerBuilderinstance.
Note: Step 2 - 4 will happen only if you use ContainerBuilder.TreatModulesAsServices() extension method directly
or indirectly by using AutofacServiceProviderFactoryDecorator.CreateDefault() method overloads.
There is a default implementation you can use.
The default implementation:
- requires your assembly file name prefixes (can be more than one), e.g.
YourPrefix,YourOtherPrefix - will search for assembly files with the following pattern:
YourPrefix.*.dll,YourOtherPrefix.*.dll
Note: EgonsoftHU prefix is always added, hence all referenced EgonsoftHU.*.dll files will also be loaded.
You can provide your own implementation. The required steps to implement:
- Initialization (ensure all relevant assemblies are loaded into the
AppDomain)
either using the parameterless ctor
or any other way that happens before configuring your implementation as the assembly registry - Getting all assemblies
either implement this interface:EgonsoftHU.Extensions.DependencyInjection.IAssemblyRegistry
or provide a public parameterless instance method that can be called using reflection.
e.g.public IEnumerable<Assembly> GetAssemblies()
Note: The method name must beGetAssemblies. The return type can be any type that can be assigned to a variable of theIEnumerable<Assembly>type.
To configure the assembly registry, use one of the ContainerBuilder extension methods below.
/*
* Use the default assembly registry.
* Provide your assembly file name prefixes.
*/
builder.UseDefaultAssemblyRegistry(nameof(YourPrefix));/*
* Use the default assembly registry.
* Provide your assembly file name prefixes.
*/
DefaultAssemblyRegistry.Initialize(nameof(YourPrefix));
builder.UseAssemblyRegistry(DefaultAssemblyRegistry.Current);/*
* The custom assembly registry must:
* - implement this interface: EgonsoftHU.Extensions.DependencyInjection.IAssemblyRegistry
* - have a parameterless ctor that initializes the instance
*/
builder.UseAssemblyRegistry<YourCustomAssemblyRegistry>();/*
* The custom assembly registry must:
* - implement this interface: EgonsoftHU.Extensions.DependencyInjection.IAssemblyRegistry
*/
IAssemblyRegistry assemblyRegistry = /* get an initialized instance of YourCustomAssemblyRegistry */
builder.UseAssemblyRegistry(assemblyRegistry);/*
* The custom assembly registry must provide a public parameterless instance method:
* - Name: GetAssemblies
* - Return type: assignable to IEnumerable<Assembly>
*/
object assemblyRegistry = /* get an initialized instance of YourCustomAssemblyRegistry */
builder.UseAssemblyRegistry(assemblyRegistry);This decorator class has static factory methods (.CreateDefault() overloads) so that the default usage will be very simple.
You can specify one or more assembly file name prefixes and zero or more dependencies (up to 4) to inject into the dependency modules.
Example configuration with 1 prefix and 2 dependencies:
using EgonsoftHU.Extensions.DependencyInjection;
// Call the IHostBuilder.UseServiceProviderFactory() method.
builder.Host.UseServiceProviderFactory(
hostBuilderContext => AutofacServiceProviderFactoryDecorator.CreateDefault(
nameof(YourPrefix),
hostBuilderContext.Configuration,
hostBuilderContext.HostingEnvironment
)
);The functionally equivalent version:
using EgonsoftHU.Extensions.DependencyInjection;
// Call the IHostBuilder.UseServiceProviderFactory() method.
builder.Host.UseServiceProviderFactory(
hostBuilderContext =>
new AutofacServiceProviderFactoryDecorator(
containerBuilder =>
containerBuilder
.UseDefaultAssemblyRegistry(nameof(YourPrefix))
.TreatModulesAsServices()
.RegisterModuleDependencyInstance(hostBuilderContext.Configuration)
.RegisterModuleDependencyInstance(hostBuilderContext.HostingEnvironment)
.RegisterModule<DependencyModule>()
)
);The following extension methods are available.
UseDefaultAssemblyRegistry(params string[] assemblyFileNamePrefixes)UseRegistryAssembly<TAssemblyRegistry>() where TAssemblyRegistry : IAssemblyRegistry, new()UseRegistryAssembly(IAssemblyRegistry assemblyRegistry)UseRegistryAssembly(object assemblyRegistry)
-
ConfigureModuleOptions(Action<ModuleOptions> setupAction)
The following options can be configured:TreatModulesAsServices(i.e. the dependency modules have dependencies, e.g.IConfiguration)
->false(default) /trueDependencyInjectionOption(how to inject dependencies into the dependency modules)
->NoInjection(default),PropertyInjection,ConstructorInjectionOnModulesRegistered(a delegate to execute after all the dependency modules are registered)
->Action<ContainerBuilder>
-
TreatModulesAsServices()
ConfiguresModuleOptions.TreatModulesAsServiceswill be set totrueDependencyInjectionOptionwill be set toModuleDependencyInjectionOption.PropertyInjection
-
RegisterModuleDependencyInstance<T>(T instance)
Registers a module dependency into a temporaryContainerBuilderfrom which it will be resolved for the dependency modules.
Please note:
- Module dependency instances are registered this way:
ContainerBuilder.RegisterInstance().ExternallyOwned() - You might register more instances of type
Tin which case set the type of the property (or the constructor parameter) toIEnumerable<T>.
This is an example for a .NET 8 MAUI project.
MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
MauiAppBuilder builder = MauiApp.CreateBuilder();
builder.ConfigureContainer(
AutofacServiceProviderFactoryDecorator.CreateDefault(
nameof(YourPrefix),
// Here a cast is needed since the type of the Configuration property is
// Microsoft.Extensions.Configuration.ConfigurationManager
(IConfiguration)builder.Configuration
)
);
// Alternatively, you can specify the type parameter.
builder.ConfigureContainer(
AutofacServiceProviderFactoryDecorator.CreateDefault<IConfiguration>(
nameof(YourPrefix),
builder.Configuration
)
);
// rest is omitted for clarity
}
}This is an example for an ASP.NET Core Web API project.
Program.cs
using EgonsoftHU.Extensions.DependencyInjection;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(
hostBuilderContext => AutofacServiceProviderFactoryDecorator.CreateDefault(
nameof(YourPrefix),
hostBuilderContext.Configuration,
hostBuilderContext.HostingEnvironment
)
);
// rest is omitted for clarityCustomService.cs (in an assembly the file name of which matches the YourPrefix.*.dll pattern)
public class CustomService : ICustomService
{
public CustomService(IOptions<MyCustomOptions> options, string environmentName)
{
}
}DependencyModule.cs (in the same assembly in which CustomService is defined)
using Autofac;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public sealed class DependencyModule : Module
{
public required IConfiguration Configuration { get; set; }
public required IHostEnvironment HostEnvironment { get; set; }
public required IServiceCollection Services { get; set; }
protected override void Load(ContainerBuilder builder)
{
Services
.AddOptions<MyCustomOptions>()
.Bind(Configuration.GetSection("MyCustomOptions"))
.ValidateDataAnnotations()
.ValidateOnStart();
builder
.RegisterType<CustomService>()
.As<ICustomService>()
.InstancePerLifetimeScope()
.WithParameter(new NamedParameter("environmentName", HostEnvironment.EnvironmentName));
}
}This is an example for an ASP.NET Web API 2 project.
WebApiConfig.cs
using System.Web.Http;
namespace YourCompany.YourProduct.WebApi
{
public static partial class WebApiConfig
{
public static void Register(HttpConfiguration httpConfiguration)
{
httpConfiguration.ConfigureAutofac();
// rest is omitted for clarity
}
}
}WebApiConfig.Extensions.Autofac.cs
using System.Configuration;
using System.Reflection;
using System.Web.Http;
using Autofac;
using Autofac.Integration.WebApi;
using EgonsoftHU.Extensions.DependencyInjection;
namespace YourCompany.YourProduct.WebApi
{
public static partial class WebApiConfig
{
public static void ConfigureAutofac(HttpConfiguration httpConfiguration)
{
var builder = new ContainerBuilder();
builder
.UseDefaultAssemblyRegistry(nameof(YourCompany))
.TreatModulesAsServices()
.RegisterModuleDependencyInstance(ConfigurationManager.AppSettings)
.RegisterModule<DependencyModule>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(httpConfiguration);
IContainer container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
}CustomService.cs (in an assembly the file name of which matches the YourPrefix.*.dll pattern)
namespace YourCompany.YourProduct.WebApi
{
public class CustomService : ICustomService
{
public CustomService(string environmentName)
{
}
}
}DependencyModule.cs (in the same assembly in which CustomService is defined)
using System.Collections.Specialized;
using Autofac;
namespace YourCompany.YourProduct.WebApi
{
public class DependencyModule : Module
{
public NameValueCollection AppSettings { get; set; }
protected override void Load(ContainerBuilder builder)
{
builder
.RegisterType<CustomService>()
.As<ICustomService>()
.InstancePerLifetimeScope()
.WithParameter(new NamedParameter("environmentName", AppSettings?["EnvironmentName"] ?? "N/A"));
}
}
}The AutofacServiceProviderFactoryDecorator is introduced in the 4.0.0 version of this package.
The example below is for the earlier package versions.
Without the decorator you need to handle service registration override issues.
Program.cs
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder
.Host
// UseSerilog()
// registers Serilog.Extensions.Logging.SerilogLoggerFactory
// as Microsoft.Extensions.Logging.ILoggerFactory
.UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(
(hostBuilderContext, containerBuilder) =>
{
// This instance will be injected into all the dependency modules.
//
// Since it does not contain the registration of SerilogLoggerFactory,
// if IServiceCollection.AddLogging() extension method is called directly or indirectly
// in the dependency modules then it will
// register Microsoft.Extensions.Logging.LoggerFactory
// as Microsoft.Extensions.Logging.ILoggerFactory.
IServiceCollection services = new ServiceCollection();
containerBuilder
.UseDefaultAssemblyRegistry(nameof(Company))
.TreatModulesAsServices()
// To avoid overriding ILoggerFactory registration made by UseSerilog()
// we remove all ILoggerFactory service registration
// from our manually created IServiceCollection instance
// before it is populated into the ContainerBuilder.
.ConfigureModuleOptions(
options =>
options.OnModulesRegistered = _ => services.RemoveAll<ILoggerFactory>()
)
.RegisterModuleDependencyInstance(services)
.RegisterModuleDependencyInstance(hostBuilderContext.Configuration)
.RegisterModuleDependencyInstance(hostBuilderContext.HostingEnvironment)
.RegisterModule<DependencyModule>();
}
);You can find some example projects in the solutions below:
- Company.Product.sln (.NET Framework 4.7.2)
ASP.NET Web API 2
- Company.Product.sln (.NET 6 / .NET 8)
ASP.NET Core Web API(.NET 6) withMicrosoft.Extensions.LoggingASP.NET Core Web API(.NET 8) withSerilogMAUI(.NET 8)