AutoMapper AddMaps重载未注册封闭泛型映射问题咨询
问题原因与解决方案
这是AutoMapper程序集扫描机制的特性限制,既不是配置疏漏也不是Bug。
核心原因
AutoMapper的AddMaps方法通过扫描指定程序集中所有继承自Profile的类型来加载映射配置,但它不会自动识别嵌套在未实例化泛型类型中的Profile类:
- 你的
MyGenericClassDto<T>是泛型类型,其内部的Mapping类属于泛型类型的嵌套成员,程序集扫描时AutoMapper无法发现这个Profile(因为泛型类型本身未被显式实例化的话,其嵌套类型不会被扫描器纳入范围)。 - 而
CreateMap是直接显式注册映射关系,完全绕过了程序集扫描逻辑,因此不受这个限制,能正常工作。
可行解决方案
方案1:将泛型DTO的Profile移到泛型类型外部
把映射配置类从泛型DTO内部移出,改为独立的非泛型Profile:
public record MyGenericClassDto<T> { public T Id { get; init; } = default!; public string Name { get; init; } = null!; } // 独立的映射配置类 private class MyGenericClassDtoMapping : Profile { public MyGenericClassDtoMapping() => CreateMap<MyClass, MyGenericClassDto<int>>(); }
这样AddMaps扫描程序集时就能正常识别到这个Profile。
方案2:显式注册泛型DTO的Profile
如果不想改动结构,可以直接用AddProfile显式指定要加载的泛型嵌套Profile:
var configuration = new MapperConfiguration(config => { config.AddMaps(Assembly.GetAssembly(typeof(MyClass))); // 显式添加泛型DTO的映射Profile config.AddProfile(typeof(MyGenericClassDto<int>.Mapping)); });
方案3:批量注册泛型映射(适用于多泛型参数场景)
如果需要支持多种Id类型的泛型DTO,可以在一个非泛型Profile中批量注册:
public class GenericDtoProfiles : Profile { public GenericDtoProfiles() { CreateMap<MyClass, MyGenericClassDto<int>>(); // 可以添加更多泛型参数的映射,比如long类型Id CreateMap<MyClass, MyGenericClassDto<long>>(); } }
修改后的测试示例(方案1)
调整后的代码可以让AddMaps的测试用例正常通过:
public class MyClass { public int Id { get; set; } public string Name { get; set; } = null!; } public record MyClassDto { public int Id { get; init; } public string Name { get; init; } = null!; private class Mapping : Profile { public Mapping() => CreateMap<MyClass, MyClassDto>(); } } public record MyGenericClassDto<T> { public T Id { get; init; } = default!; public string Name { get; init; } = null!; } // 独立的泛型DTO映射配置 private class MyGenericClassDtoMapping : Profile { public MyGenericClassDtoMapping() => CreateMap<MyClass, MyGenericClassDto<int>>(); } public class MappingTests { [Test] [TestCase(typeof(MyClass), typeof(MyClassDto))] [TestCase(typeof(MyClass), typeof(MyGenericClassDto<int>))] public void ShouldSupportMappingFromSourceToDestination_AddMaps( Type source, Type destination) { var instance = GetInstanceOf(source); var configuration = new MapperConfiguration(config => config.AddMaps(Assembly.GetAssembly(typeof(MyClass)))); var mapper = configuration.CreateMapper(); mapper.Map(instance, source, destination); } // 原CreateMap测试保持不变 [Test] [TestCase(typeof(MyClass), typeof(MyClassDto))] [TestCase(typeof(MyClass), typeof(MyGenericClassDto<int>))] public void ShouldSupportMappingFromSourceToDestination_CreateMap( Type source, Type destination) { var instance = GetInstanceOf(source); var config = new MapperConfiguration(cfg => cfg.CreateMap(source, destination)); var mapper = new Mapper(config); mapper.Map(instance, source, destination); } private static object GetInstanceOf(Type type) => type.GetConstructor(Type.EmptyTypes) != null ? Activator.CreateInstance(type)! : RuntimeHelpers.GetUninitializedObject(type); }
内容的提问来源于stack exchange,提问作者JD Davis
相关产品推荐
相关产品推荐

