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 registration rule for dependency injection container, only once so that it can cover all existing, and future repositories, without additional effort.
This is something we want to avoid:
1
2
3
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:
1
2
3
4
5
6
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 wont 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:
1
2
3
4
5
6
7
8
9
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 this block of code do actually?
First we get all types from the assembly:
1
2
3
4
...
Assembly.GetExecutingAssembly()
.GetTypes()
...
then we filter those types to the repository classes
1
2
3
...
.Where(a => a.Name.EndsWith("Repository") && !a.IsAbstract && !a.IsInterface)
...
and we define pairs of types with the list of interfaces they implement
1
2
3
4
...
.Select(a => new { assignedType = a, serviceTypes = a.GetInterfaces().ToList() })
.ToList()
...
at the end we just itterate through the pairs of types and register each of them as the interface they implement
1
2
3
4
5
6
...
.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 DI container of each type.