Registering all types as generic interfaces in assembly in dotnet core
Let’s say that we have an interface in your app as IRepository<T>
which has dozens of implementations. Wouldn’t it be so nice to define the registration rule for dependency injection containers, only once so that it can cover all existing, and future repositories, without additional effort?
This is something we want to avoid:
serviceCollection.AddTransient(typeof(IRepository<User>), typeof(UserRepository));
serviceCollection.AddTransient(typeof(IRepository<Company>), typeof(CompanyRepository));
serviceCollection.AddTransient(typeof(IRepository<Car>), typeof(CarRepository));
If you’ve used Autofac before, something like this could be defined in a couple of lines:
public static void UseRepositories(this ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsClosedTypesOf(typeof(IRepository<>))
.AsImplementedInterfaces();
}
Basically, you would get current assembly, select all types implementing IRepository<>
and register them as interface they implement, which is at least IRepository<>
.
After this, we’re able to inject UserRepository
as IRepository<User>
.
In dotnet core, you won't use Autofac most probably. We still don’t have those handy methods like AsClosedTypesOf
or AsImplementedInterfaces
therefore, we’d have to implement it on our own, something like this:
Assembly.GetExecutingAssembly()
.GetTypes()
.Where(a => a.Name.EndsWith("Repository") && !a.IsAbstract && !a.IsInterface)
.Select(a => new { assignedType = a, serviceTypes = a.GetInterfaces().ToList() })
.ToList()
.ForEach(typesToRegister =>
{
typesToRegister.serviceTypes.ForEach(typeToRegister => services.AddScoped(typeToRegister, typesToRegister.assignedType));
});
What does this block of code do actually?
First, we get all types from the assembly:
...
Assembly.GetExecutingAssembly()
.GetTypes()
...
then we filter those types to the repository classes
...
.Where(a => a.Name.EndsWith("Repository") && !a.IsAbstract && !a.IsInterface)
...
and we define pairs of types with the list of interfaces they implement
...
.Select(a => new { assignedType = a, serviceTypes = a.GetInterfaces().ToList() })
.ToList()
...
in the end, we just iterate through the pairs of types and register each of them as the interface they implement
...
.ForEach(typesToRegister =>
{
typesToRegister.serviceTypes.ForEach(typeToRegister => services.AddScoped(typeToRegister, typesToRegister.assignedType));
});
...
Now we’re able to inject UserRepository
as IRepository<User>
, without the need for explicit registration in the DI containers of each type.