通过通用接口/抽象 class 实现使 .NET Core DI 自动解析 class
Make .NET Core DI auto resolve class by generic interface / abstract class implementation
.NET Core 中有没有办法注册通用接口,并使其解析与特定实现匹配的 class。
比如我有如下界面:
public interface IMapper<TFrom, TTo>
{
}
我也有一个摘要class:
public abstract class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
protected Mapper()
{
// some generic stuff
}
public abstract TTo Map(TFrom);
}
然后我可以像这样创建一个实现:
public class UserMapper : Mapper<Domain.User, Entity.User>
{
public override Entity.User Map(Domain.User from)
{
// do mapping
}
}
有没有办法,使用默认的.NET Core DI注册IMapper<,>
,让它自动解析class?
因此,如果我在代码中的某处执行此操作:
class SomeClass
{
public SomeClass(IMapper<Domain.User, Entity.User> mapper) {}
}
它以某种方式知道它应该解析 class UserMapper<Domain.User, Entity.User>
?
原因是手动注册每个特定于实现的映射器有点冗长。所以我希望 Microsoft.DependencyInjection
足够聪明,能够以某种方式自动解决它的实现。
您当前设计的唯一方法是使用反射:
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 for AddTransient
, AddSingleton
, etc.
如果您更改设计,请使用 non-abstract 泛型作为您的实现类型:
public class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
//...
}
那么您可以像这样简单地注册:
services.AddTransient(typeof(IMapper<,>), typeof(Mapper<,>));
这是一个更通用的解决方案(默认作用域生命周期)
先创建这个扩展方法
public static void RegisterAllTypes(this IServiceCollection services, Assembly assembly, Type baseType,
ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
foreach (var type in assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract))
{
foreach (var i in type.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == baseType))
{
var interfaceType = baseType.MakeGenericType(i.GetGenericArguments());
services.Add(new ServiceDescriptor(interfaceType, type, lifetime));
}
}
}
然后将其添加到 startup.cs
services.RegisterAllTypes(typeof(AClass).Assembly, typeof(IMapper<>));
这里你已经准备好使用助手了:
通过通用接口
services.AddAllGenericTypes(typeof(IGenericRepository<>), new[] {typeof(MyDbContext).GetTypeInfo().Assembly});
通过界面
services.AddAllTypes<IGenerator>(new[] { typeof(FileHandler).GetTypeInfo().Assembly },
扩展名来自:
https://gist.github.com/GetoXs/5caf0d8cfe6faa8a855c3ccef7c5a541
.NET Core 中有没有办法注册通用接口,并使其解析与特定实现匹配的 class。
比如我有如下界面:
public interface IMapper<TFrom, TTo>
{
}
我也有一个摘要class:
public abstract class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
protected Mapper()
{
// some generic stuff
}
public abstract TTo Map(TFrom);
}
然后我可以像这样创建一个实现:
public class UserMapper : Mapper<Domain.User, Entity.User>
{
public override Entity.User Map(Domain.User from)
{
// do mapping
}
}
有没有办法,使用默认的.NET Core DI注册IMapper<,>
,让它自动解析class?
因此,如果我在代码中的某处执行此操作:
class SomeClass
{
public SomeClass(IMapper<Domain.User, Entity.User> mapper) {}
}
它以某种方式知道它应该解析 class UserMapper<Domain.User, Entity.User>
?
原因是手动注册每个特定于实现的映射器有点冗长。所以我希望 Microsoft.DependencyInjection
足够聪明,能够以某种方式自动解决它的实现。
您当前设计的唯一方法是使用反射:
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.
如果您更改设计,请使用 non-abstract 泛型作为您的实现类型:
public class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
//...
}
那么您可以像这样简单地注册:
services.AddTransient(typeof(IMapper<,>), typeof(Mapper<,>));
这是一个更通用的解决方案(默认作用域生命周期)
先创建这个扩展方法
public static void RegisterAllTypes(this IServiceCollection services, Assembly assembly, Type baseType,
ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
foreach (var type in assembly.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract))
{
foreach (var i in type.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == baseType))
{
var interfaceType = baseType.MakeGenericType(i.GetGenericArguments());
services.Add(new ServiceDescriptor(interfaceType, type, lifetime));
}
}
}
然后将其添加到 startup.cs
services.RegisterAllTypes(typeof(AClass).Assembly, typeof(IMapper<>));
这里你已经准备好使用助手了:
通过通用接口
services.AddAllGenericTypes(typeof(IGenericRepository<>), new[] {typeof(MyDbContext).GetTypeInfo().Assembly});
通过界面
services.AddAllTypes<IGenerator>(new[] { typeof(FileHandler).GetTypeInfo().Assembly },
扩展名来自: https://gist.github.com/GetoXs/5caf0d8cfe6faa8a855c3ccef7c5a541