Is there a way in .NET Core to register a generic interface, and make it resolve a class that matches a certain implementation.
For example, I have the following interface:
public interface IMapper<TFrom, TTo>
{
}
I also have an abstract class:
public abstract class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
protected Mapper()
{
// some generic stuff
}
public abstract TTo Map(TFrom);
}
I can then create an implementation like so:
public class UserMapper : Mapper<Domain.User, Entity.User>
{
public override Entity.User Map(Domain.User from)
{
// do mapping
}
}
Is there a way, using the default .NET Core DI to register IMapper<,>
, and let it auto resolve the class?
So if I for example would do this somewhere in code:
class SomeClass
{
public SomeClass(IMapper<Domain.User, Entity.User> mapper) {}
}
That it somehow knows that it should resolve the class UserMapper<Domain.User, Entity.User>
?
The reason is that it's a little verbose to manually register each and every mapper, specific to an implementation. So I'm hoping Microsoft.DependencyInjection
is smart enough to automatically resolve its implementation somehow.
The only way with your current design is to use Reflection:
Assembly assembly = typeof(UserMapper).Assembly;
foreach (var type in assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract))
{
foreach (var i in type.GetInterfaces())
{
if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapper<,>))
{
// NOTE: Due to a limitation of Microsoft.DependencyInjection we cannot
// register an open generic interface type without also having an open generic
// implementation type. So, we convert to a closed generic interface
// type to register.
var interfaceType = typeof(IMapper<,>).MakeGenericType(i.GetGenericArguments());
services.AddTransient(interfaceType, type);
}
}
}
NOTE: You could make it simpler by creating an extension method on
IServiceCollection
with the various overloads forAddTransient
,AddSingleton
, etc.
If you change the design use a non-abstract generic as your implementation type:
public class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
//...
}
Then you can simply register like this:
services.AddTransient(typeof(IMapper<,>), typeof(Mapper<,>));