Autofac is a popular dependency injection library that I like to use because they’ve done a lot of legwork to get things to work nicely in .NET Core – (some DI frameworks were not happy with Microsoft’s decision to make a public abstraction for their own DI library).
Modules
If you have a big solution with several different projects, it can be easier to manage each project’s registrations and encapsulate them with an Autofac Module. Then you can register all of the modules together in the application entry point, making registration management a little bit easier.
You can make a module by inheriting Autofac.Module, which is an abstract class that provides some overridable virtual functions to facilitate registration. One of these is ThisAssembly, which is just a shortcut to the assembly containing the module. This is pretty useful as you can use ThisAssembly to scan through and automatically register types based on any criteria you can think of (such as naming conventions or attributes).
public class MyModule : Autofac.Module | |
{ | |
protected override void Load(ContainerBuilder builder) | |
{ | |
// ThisAssembly points to the assembly that contains this module | |
builder.RegisterTypes(ThisAssembly.GetTypes() | |
// Find all "Services" | |
.Where(t => t.Name.EndsWith("Service")).ToArray()) | |
.AsImplementedInterfaces() | |
.SingleInstance(); | |
} | |
} |
Module Base Class
Suppose that you have common registration techniques that you want to just have automatically registered. You could create a base class that does the common registration for you, meaning that all you need to do is any project-specific registration.
public abstract class ModuleBase: Module | |
{ | |
// Use the "sealed" keyword to prevent future overloading. You might | |
// want to this so the inheritors of this base class don't accidentally | |
// override the Load method, which Autofac calls automatically when you | |
// register a module. | |
protected sealed override void Load(ContainerBuilder builder) | |
{ | |
builder.RegisterTypes(ThisAssembly.GetTypes() | |
.Where(t => t.Name.EndsWith("Service")).ToArray()) | |
.AsImplementedInterfaces() | |
.SingleInstance(); | |
// Call the other virtual function | |
AdditionalDependencies(builder); | |
} | |
// Provide a different virtual function to register any additional types | |
protected virtual void AdditionalDependencies(ContainerBuilder builder) | |
{ | |
} | |
} |
public class MyModule : ModuleBase | |
{ | |
protected override void AdditionalDependencies(ContainerBuilder builder) | |
{ | |
// Register any other stuff here! | |
builder.RegisterTypes(ThisAssembly.GetTypes() | |
.Where(t => t.Name.EndsWith("Repository")).ToArray()) | |
.AsImplementedInterfaces() | |
.SingleInstance(); | |
} | |
} |
Now it is even easier to deal with registrations!
… but there’s a problem:
You can’t use ThisAssembly unless you’re inheriting directly from Module. This makes sense when you think about it – you don’t want ThisAssembly to resolve to the assembly containing the ModuleBase class… it would sort-of defeat the purpose, and cause all kinds of issues during runtime.
Fixing the Base Class
Luckily, since Autofac.Module is abstract with its important bits marked as virtual, its relatively easy to fix. All we need to do is override ThisAssembly to match the assembly of the final inherited type.
public abstract class ModuleBase<T>: Module where T: ModuleBase<T> | |
{ | |
// We have to override ThisAssembly because Autofac gets upset once you end up nesting | |
// Module inheritance. We do ModuleBase<T> so we can get the assembly of the top-level module | |
protected sealed override Assembly ThisAssembly => typeof(T).Assembly; | |
protected sealed override void Load(ContainerBuilder builder) | |
{ | |
builder.RegisterTypes(ThisAssembly.GetTypes().Where(t => t.Name.EndsWith("Service")).ToArray()) | |
.AsImplementedInterfaces() | |
.SingleInstance(); | |
AdditionalDependencies(builder); | |
} | |
protected virtual void AdditionalDependencies(ContainerBuilder builder) | |
{ | |
} | |
} |
// Just have to add <MyModule> now to get things to work | |
public class MyModule : ModuleBase<MyModule> | |
{ | |
protected override void AdditionalDependencies(ContainerBuilder builder) | |
{ | |
// Register any other stuff here! | |
builder.RegisterTypes(ThisAssembly.GetTypes() | |
.Where(t => t.Name.EndsWith("Repository")).ToArray()) | |
.AsImplementedInterfaces() | |
.SingleInstance(); | |
} | |
} |
Doing this lets us get the type and thus the assembly of the top-level module, allowing ThisAssembly to point to what it normally would if we were inheriting Autofac.Module directly.
Autofac–Automatically registering generics via constructors – Darchuk.NET
[…] in this blog post I talked about using Autofac Modules to reduce your headaches and register things automatically. Since we are using constructor […]