.NET Core DI如何自动解析泛型接口对应的实现类?
绝对没问题,.NET Core 自带的依赖注入容器对泛型类型的注册和解析支持得非常好。针对你提到的 IMapper<TSource, TDestination> 这类泛型接口,不管是单个实现类还是批量的实现类,都能轻松实现自动匹配。
我分几种场景给你讲具体的实现方式:
场景1:少量 Mapper 实现类,手动注册
如果你的 Mapper 类数量不多,直接手动注册最直观。比如你有 UserMapper : IMapper<Domain.User, Entity.User>,只需要在 .NET 6+ 的 Program.cs(或者旧版本的 Startup.cs)里加一行:
services.AddScoped<IMapper<Domain.User, Entity.User>, UserMapper>();
之后当你在构造函数中请求 IMapper<Domain.User, Entity.User> 时,DI 容器就会自动返回 UserMapper 的实例。生命周期可以根据你的需求改成 AddTransient(每次请求新实例)或者 AddSingleton(全局单例)。
场景2:大量 Mapper 实现类,批量自动注册
如果你的项目中有几十个甚至上百个 Mapper 类,逐个手动注册太麻烦了。这时候可以用反射扫描程序集的方式,自动找到所有实现了 IMapper<,> 的类并注册到 DI 容器中。
具体步骤:
- 先确保你的所有 Mapper 类都明确实现了
IMapper<TSource, TDestination>(比如UserMapper实现IMapper<Domain.User, Entity.User>,而非泛型类)。 - 用反射扫描包含这些 Mapper 类的程序集,筛选出符合条件的类型,批量注册。
示例代码如下(.NET 6+ Program.cs 中使用):
// 获取包含你的 Mapper 类的程序集(这里以 UserMapper 所在的程序集为例) var mapperAssembly = typeof(UserMapper).Assembly; // 扫描所有实现了 IMapper<,> 的非抽象类 var mapperTypes = mapperAssembly.GetTypes() .Where(type => !type.IsAbstract && !type.IsGenericTypeDefinition) .Select(type => new { Implementation = type, // 找到该类实现的 IMapper<,> 接口 MapperInterface = type.GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapper<,>)) }) .Where(item => item.MapperInterface != null); // 批量注册到 DI 容器 foreach (var mapper in mapperTypes) { // 这里用 Scoped 生命周期,你可以根据需求替换成 Transient/Singleton services.AddScoped(mapper.MapperInterface, mapper.Implementation); }
这段代码会自动扫描程序集里所有符合条件的 Mapper 类,把它们和对应的 IMapper<TSource, TDestination> 接口绑定起来。之后你请求任何匹配的泛型接口,DI 容器都会自动返回对应的实现类实例。
额外场景:泛型 Mapper 实现类
如果你的项目中用的是泛型实现类(比如 public class GenericMapper<TSource, TDestination> : IMapper<TSource, TDestination>),那注册方式更简单:
services.AddScoped(typeof(IMapper<,>), typeof(GenericMapper<,>));
这样当你请求 IMapper<OrderDomain, OrderEntity> 时,DI 容器会自动创建 GenericMapper<OrderDomain, OrderEntity> 的实例,完全不需要手动注册每个具体的泛型组合。
注意事项
- 确保每个实现类只对应一个
IMapper<TSource, TDestination>接口实例,否则反射扫描时可能会出现匹配错误(如果一个类实现了多个泛型接口的话)。 - 生命周期的选择要符合业务逻辑:比如涉及数据库操作的 Mapper 用 Scoped 比较合适,无状态的工具类可以用 Singleton。
- 如果有多个实现类对应同一个泛型接口(比如两个不同的
IMapper<User, UserDto>),DI 容器默认会返回最后注册的那个。这种情况你需要用命名服务或者工厂模式来区分不同的实现。
内容的提问来源于stack exchange,提问作者Vivendi

