阅读目录
1、介绍
2、快速入门
3、功能应用
3.1、配置
3.2、自定义映射属性配对
3.3、条件映射
3.4、自定义映射前后事件
3.5、自定义类型转换
3.6、自定义值解析器
3.7、值转换器
3.8、泛型映射
3.9、枚举映射
4、参考
AutoMapper 是一个简单的小库,基于命名约定的对象到对象的映射工具。只要2个对象的属性具有相同名字(或者符合它规定的命名约定),AutoMapper就可以替我们自动在2个对象间进行属性值的映射。如果有不符合约定的属性,或者需要自定义映射行为,就需要我们事先告诉AutoMapper,所以在使用 Map(src,dest)进行映射之前,必须使用 CreateMap() 进行配置。注意:将源映射到目标时,AutoMapper 将忽略空引用异常,可以通过自定义解析器来更改这种设置。
使用之前需要Nuget引入 AutoMapper 11.0.0 ,分别创建源对象UserInfo和目标对象UserInfoDto,建立映射关系后把源对象映射成目标对象。注意:经验法则是一个应用程序域AppDomian只需要一个AutoMapper配置对象 MapperConfiguration 实例,并且应该在启动初期进行实例化。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.GettingStarted 8 { 9 public class UserInfo 10 { 11 public string Name { get; set; } 12 public int Age { get; set; } 13 public bool Sex { get; set; } 14 public decimal Salary { get; set; } //薪水 15 16 } 17 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.GettingStarted 8 { 9 public class UserInfoDto 10 { 11 public string Name { get; set; } 12 public int Age { get; set; } 13 public bool Sex { get; set; } 14 public override string ToString() 15 { 16 return string.Join(",", this.GetType().GetProperties().Select(p => p.Name).ToArray()); 17 } 18 } 19 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.GettingStarted 9 { 10 public class GettingStartedMain 11 { 12 public static void Test1() 13 { 14 //1、创建映射规则 15 var config = new MapperConfiguration(cfg => cfg.CreateMap<UserInfo, UserInfoDto>()); 16 var mapper = config.CreateMapper();//或者:var mapper = new Mapper(config); 17 //2、把对象映射成需要的对象 18 var userInfo = new UserInfo { Name = "BigBox777", Age = 18, Sex = true, Salary = 1.00m }; 19 UserInfoDto userInfoDto = mapper.Map<UserInfoDto>(userInfo); 20 Console.WriteLine(userInfoDto.ToString()); 21 } 22 } 23 }
通常会新建一个配置类用来存放AutoMapper映射配置,下面例子是新建一个AutoMapperProfile类来存放AutoMapper的配置关系,注意:①AutoMapperProfile类必须继承Profile。②经验法则是一个应用程序域AppDomian只需要一个AutoMapper配置对象 MapperConfiguration 实例,并且应该在启动初期进行实例化。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.AutoMapperConfiguration 8 { 9 public class Student 10 { 11 public string Name { get; set; } 12 public string StudentNumber { get; set; } 13 public double Stature { get; set; } //身高 14 public int ChemistryAchievement { get; set; } 15 public int MathematicsAchievement { get; set; } 16 public int EnglishAchievement { get; set; } 17 } 18 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.AutoMapperConfiguration 8 { 9 public class StudentDto 10 { 11 public string Name { get; set; } 12 public string StudentNumber { get; set; } 13 public int ChemistryAchievement { get; set; } 14 public override string ToString() 15 { 16 return string.Join(",", this.GetType().GetProperties().Select(p => p.Name).ToArray()); 17 } 18 } 19 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class AutoMapperConfigurationMain 11 { 12 public static void Test1() 13 { 14 //扫描读取配置类 15 var config = new MapperConfiguration( 16 cfg => cfg.AddProfile<AutoMapperProfile>()); 17 var mapper = config.CreateMapper(); 18 var student = new Student() 19 { 20 Name = "BigBox777", 21 StudentNumber = "668", 22 Stature = 198, 23 ChemistryAchievement = 100, 24 MathematicsAchievement = 100, 25 EnglishAchievement = 100, 26 }; 27 StudentDto studentDto = mapper.Map<StudentDto>(student); 28 Console.WriteLine(studentDto.ToString()); 29 } 30 31 } 32 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class AutoMapperConfigurationMain 11 { 12 public static void Test2() 13 { 14 //AutoMapper 将扫描指定的程序集以查找从 Profile 继承的类并将它们添加到配置中 15 var configuration1 = new MapperConfiguration(cfg => 16 cfg.AddMaps(myAssembly)); 17 18 var configuration2 = new MapperConfiguration(cfg => 19 cfg.AddMaps(new[] { "Foo.UI", "Foo.Core" })); 20 21 // Or marker types for assemblies: 22 var configuration = new MapperConfiguration(cfg => 23 cfg.AddMaps(new[] { typeof(HomeController), typeof(Entity)}); 24 25 } 26 } 27 }
上面的例子都是源类和目标类的字段属性完全一样的情况下的简单映射,本节来介绍通过配置基类 Profile 手动配置源类和目标类映射关系。主要会用到 禁用构造函数映射public void DisableConstructorMapping() 、 清除源属性字段前缀public void ClearPrefixes() 、 替换源字符串public void ReplaceMemberName(string original, string newValue) 、 去除源属性前缀public void RecognizePrefixes(params string[] prefixes) 、 去除源属性后缀public void RecognizePostfixes(params string[] postfixes) 、 去除目标属性前缀public void RecognizeDestinationPrefixes(params string[] prefixes) 、 去除目标属性后缀public void RecognizeDestinationPostfixes(params string[] postfixes) 、 全局忽略源属性public void AddGlobalIgnore(string propertyNameStartingWith) 、 包和源扩展方法public void IncludeSourceExtensionMethods(Type type) 。
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class NamingConventionsProfile:Profile 11 { 12 public NamingConventionsProfile() 13 { 14 //建立属性字段对应关系: property_name -> PropertyName 15 SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); 16 DestinationMemberNamingConvention = new PascalCaseNamingConvention(); 17 CreateMap<Source, Dest>(); 18 } 19 } 20 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 public class ReplacingCharactersProfile: Profile 11 { 12 public ReplacingCharactersProfile() 13 { 14 //属性匹配之间的字符替换 15 ReplaceMemberName("Ä", "A"); 16 ReplaceMemberName("í", "i"); 17 ReplaceMemberName("Airlina", "Airline"); 18 CreateMap<Source, Destination>(); 19 } 20 public class Source 21 { 22 public int Value { get; set; } 23 public int Ävíator { get; set; } 24 public int SubAirlinaFlight { get; set; } 25 } 26 public class Destination 27 { 28 public int Value { get; set; } 29 public int Aviator { get; set; } 30 public int SubAirlineFlight { get; set; } 31 } 32 } 33 34 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class RecognizingPreProfile:Profile 11 { 12 public RecognizingPreProfile() 13 { 14 //去除源属性中的frm 15 RecognizePrefixes("frm"); 16 CreateMap<Source, Dest>(); 17 } 18 public class Source 19 { 20 public int frmValue { get; set; } 21 public int frmValue2 { get; set; } 22 } 23 public class Dest 24 { 25 public int Value { get; set; } 26 public int Value2 { get; set; } 27 } 28 } 29 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class RecognizingPostfixesProfile:Profile 11 { 12 //目标属性后加Dto 13 public RecognizingPostfixesProfile() 14 { 15 RecognizeDestinationPostfixes("Dto"); 16 CreateMap<Source, Dest>(); 17 } 18 public class Source 19 { 20 public int Value { get; set; } 21 public int Value2 { get; set; } 22 } 23 public class Dest 24 { 25 public int ValueDto { get; set; } 26 public int Value2Dto { get; set; } 27 } 28 } 29 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class GlobalPropertyFieldFilteringProfile:Profile 11 { 12 //全局属性字段过滤 13 public GlobalPropertyFieldFilteringProfile() 14 { 15 //不映射任何字段 16 ShouldMapField = fi => false; 17 //映射Public、Private的所有带Getter器的属性 18 ShouldMapProperty = pi => 19 pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate); 20 CreateMap<Student, StudentDto>(); 21 } 22 } 23 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class ConfiguringVisibilityProfile:Profile 11 { 12 //配置可见性(默认情况下,AutoMapper 只识别公共成员。它可以映射到私有设置器, 13 //但如果整个属性是私有/内部的,它将跳过内部/私有方法和属性。要指示 AutoMapper 14 //识别具有其他可见性的成员,请覆盖默认过滤器 ShouldMapField 和/或 ShouldMapProperty ) 15 public ConfiguringVisibilityProfile() 16 { 17 ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly; 18 CreateMap<Student, StudentDto>(); 19 } 20 } 21 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class ConfigurationCompilationProfile:Profile 11 { 12 //配置编译:因为表达式编译可能会占用一些资源,所以 AutoMapper 会在第一个映射上 13 //延迟编译类型映射计划。然而,这种行为并不总是可取的,所以你可以告诉 AutoMapper 14 //直接编译它的映射 15 public ConfigurationCompilationProfile() 16 { 17 CreateMap<Student,StudentDto>(); 18 } 19 } 20 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.AutoMapperConfiguration 9 { 10 internal class AutoMapperConfigurationMain 11 { 12 public static void Test5() 13 { 14 var config = new MapperConfiguration( 15 cfg => cfg.AddProfile<ConfigurationCompilationProfile>()); 16 config.CompileMappings(); 17 var mapper = config.CreateMapper(); 18 var student = new Student() 19 { 20 Name = "BigBox777", 21 StudentNumber = "668", 22 Stature = 198, 23 ChemistryAchievement = 100, 24 MathematicsAchievement = 100, 25 EnglishAchievement = 100, 26 }; 27 StudentDto studentDto = mapper.Map<StudentDto>(student); 28 Console.WriteLine(studentDto.ToString()); 29 30 } 31 } 32 }
在满足一定条件才允许发生映射,举例:源类型是int,对应的目标类型是uint,需要源int的属性值大于等于0才能映射,不满足条件时用属性的默认值填充。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.ConditionalMapping 8 { 9 public class Source 10 { 11 public int Number { get; set; } 12 } 13 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.ConditionalMapping 8 { 9 public class Dest 10 { 11 public uint Number { get; set; } 12 } 13 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.ConditionalMapping 9 { 10 internal class ConditionalMappingProfile:Profile 11 { 12 //转换之前,源Number属性值大于等于0才能转换,不符合条件时用默认值 13 public ConditionalMappingProfile() 14 { 15 CreateMap<Source, Dest>().ForMember( 16 dest => dest.Number, 17 opt => opt.PreCondition(src => src.Number >= 0)); 18 } 19 } 20 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.ConditionalMapping 9 { 10 internal class ConditionalMappingMain 11 { 12 public static void Test1() 13 { 14 var config = new MapperConfiguration(cfg => cfg.AddProfile<ConditionalMappingProfile>()); 15 var mapper = config.CreateMapper(); 16 var source = new Source { Number = 3 }; 17 var result = mapper.Map<Dest>(source); 18 } 19 } 20 }
我们可以在映射发生之前和映射发生之后定义部分事件操作。看下面的例子
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.BeforeAfterMapAction 8 { 9 internal class Source 10 { 11 public string Number1 { get; set; } 12 public int Number2 { get; set; } 13 } 14 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.BeforeAfterMapAction 8 { 9 internal class Dest 10 { 11 public string Number1 { get; set; } 12 public int Number2 { get; set; } 13 } 14 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.BeforeAfterMapAction 9 { 10 internal class BeforeAfterMapActionProfile:Profile 11 { 12 //定义映射之前和之后的动作 13 public BeforeAfterMapActionProfile() 14 { 15 CreateMap<Source, Dest>() 16 .BeforeMap((src, dest) => { src.Number2++;}) 17 .AfterMap((src, dest) => { 18 src.Number1 = "BigBox777" + src.Number1; 19 dest.Number1 = dest.Number1 + "BigBox777"; 20 dest.Number2--; 21 }); 22 } 23 } 24 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.BeforeAfterMapAction 9 { 10 internal class BeforeAfterMapActionMain 11 { 12 //方式1:配置文件里定义映射前后的动作 13 public static void Test1() 14 { 15 var config = new MapperConfiguration(src=>src.AddProfile<BeforeAfterMapActionProfile>()); 16 var mapper = config.CreateMapper(); 17 var source = new Source { Number1 = "真棒", Number2 = 8 }; 18 var result = mapper.Map<Dest>(source); 19 } 20 } 21 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.BeforeAfterMapAction 9 { 10 internal class UsingIMappingActionProfile:Profile 11 { 12 public UsingIMappingActionProfile() 13 { 14 CreateMap<Source, Dest>().AfterMap<NameMappingAction>(); 15 } 16 } 17 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.BeforeAfterMapAction 9 { 10 //Source=》Dest定义映射动作 11 internal class NameMappingAction : IMappingAction<Source, Dest> 12 { 13 public void Process(Source source, Dest destination, ResolutionContext context) 14 { 15 source.Number1 = "BigBox777" + source.Number1; 16 destination.Number1= destination.Number1 + "BigBox777"; 17 } 18 } 19 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.BeforeAfterMapAction 9 { 10 internal class BeforeAfterMapActionMain 11 { 12 //方式2:新增一个动作类,继承接口IMappingAction 13 public static void Test2() 14 { 15 var config = new MapperConfiguration(src => src.AddProfile<UsingIMappingActionProfile>()); 16 var mapper = config.CreateMapper(); 17 var source = new Source { Number1 = "真棒", Number2 = 8 }; 18 var result = mapper.Map<Dest>(source); 19 } 20 } 21 }
需要注意在 Asp.Net Core 和 AutoMapper.Extensions.Microsoft.DependencyInjection 项目中,不能将依赖注入到Profile类中,但可以在 IMappingAction 实现中进行。
1 public class SetTraceIdentifierAction : IMappingAction<SomeModel, SomeOtherModel> 2 { 3 private readonly IHttpContextAccessor _httpContextAccessor; 4 5 public SetTraceIdentifierAction(IHttpContextAccessor httpContextAccessor) 6 { 7 _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); 8 } 9 10 public void Process(SomeModel source, SomeOtherModel destination, ResolutionContext context) 11 { 12 destination.TraceIdentifier = _httpContextAccessor.HttpContext.TraceIdentifier; 13 } 14 } 15 16 public class SomeProfile : Profile 17 { 18 public SomeProfile() 19 { 20 CreateMap<SomeModel, SomeOtherModel>() 21 .AfterMap<SetTraceIdentifierAction>(); 22 } 23 }
有时需要完全控制一种类型到另一种类型的转换。这通常是当一种类型看起来与另一种完全不同时,已经存在转换函数,并且您希望从“更宽松”的类型转换为更强的类型,下面例子,使用自定义了类型转换器,自定义转换器需要继承泛型接口 public interface ITypeConverter<in TSource, TDestination> 。注意:自定义类型转换器是全局的,只要 AutoMapper 在任何映射类型上找到源/目标对,就可以使用它们。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.CustomTypeConverters 8 { 9 public class Source 10 { 11 public string Value1 { get; set; } 12 public string Value2 { get; set; } 13 public string Value3 { get; set; } 14 } 15 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.CustomTypeConverters 8 { 9 public class Dest 10 { 11 public int Value1 { get; set; } 12 public DateTime Value2 { get; set; } 13 public Type Value3 { get; set; } 14 } 15 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.CustomTypeConverters 9 { 10 //自己写的类型转换器:string=>DateTime 11 public class DateTimeTypeConverter : ITypeConverter<string, DateTime> 12 { 13 public DateTime Convert(string source, DateTime destination, ResolutionContext context) 14 { 15 destination = DateTime.Parse(source); 16 return destination; 17 } 18 } 19 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.CustomTypeConverters 9 { 10 //自定义类型转换器:string=》Type 11 internal class TypeConverter : ITypeConverter<string, Type> 12 { 13 public Type Convert(string source, Type destination, ResolutionContext context) 14 { 15 destination=source.GetType(); 16 return destination; 17 } 18 } 19 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.CustomTypeConverters 9 { 10 internal class CustomTypeConvertersProfile:Profile 11 { 12 public CustomTypeConvertersProfile() 13 { 14 //用现成的转换器 15 CreateMap<string,int>().ConvertUsing(str=>Convert.ToInt32(str)); 16 //自己写一个类型转换器 17 CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter()); 18 //自己写的类型转换器 19 CreateMap<string,Type>().ConvertUsing<TypeConverter>(); 20 CreateMap<Source, Dest>(); 21 } 22 } 23 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.CustomTypeConverters 9 { 10 internal class CustomTypeConvertersMain 11 { 12 public static void Test1() 13 { 14 var config = new MapperConfiguration(cfg => 15 cfg.AddProfile<CustomTypeConvertersProfile>()); 16 var mapper = config.CreateMapper(); 17 var source = new Source { Value1 = "872", Value2 = "2000-1-1", Value3 = "China" }; 18 var dest = mapper.Map<Dest>(source); 19 } 20 } 21 }
有时候目标类型的某属性需要通过源类型的多个属性进行特殊计算处理得到,这个时候就需要使用自定义解析器了。自定值解析器 CustomTotalResolver 需要继承接口 public interface IValueResolver<in TSource, in TDestination, TDestMember> ,最后在配置类里配置当解析特定目标成员时使用这个自定义值解析器。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.CustomValueResolvers 8 { 9 public class Source 10 { 11 public int Value1 { get; set; } 12 public int Value2 { get; set; } 13 } 14 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.CustomValueResolvers 8 { 9 public class Dest 10 { 11 public int Total { get; set; } 12 } 13 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.CustomValueResolvers 9 { 10 //自定义值解析器需要继承IValueResolver接口 11 public class CustomTotalResolver : IValueResolver<Source, Dest, int> 12 { 13 public int Resolve(Source source, Dest destination, int destMember, ResolutionContext context) 14 { 15 return source.Value1 + source.Value2; 16 } 17 } 18 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.CustomValueResolvers 9 { 10 internal class CustomValueResolversProfile:Profile 11 { 12 public CustomValueResolversProfile() 13 { 14 //指明解析Dest的Total属性时使用解析器CustomTotalResolver 15 CreateMap<Source, Dest>(). 16 ForMember(dest => dest.Total, opt => opt.MapFrom<CustomTotalResolver>()); 17 } 18 } 19 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.CustomValueResolvers 8 { 9 internal class CustomValueResolversMain 10 { 11 public static void Test1() 12 { 13 var config = new AutoMapper.MapperConfiguration(cfg => cfg.AddProfile<CustomValueResolversProfile>()); 14 var mapper = config.CreateMapper(); 15 var source = new Source { Value1 = 123, Value2 = 234 }; 16 var dest = mapper.Map<Dest>(source); 17 } 18 } 19 }
1 //直接提供解析器实例 2 CreateMap<Source, Dest>().ForMember(dest => dest.Total, opt => opt.MapFrom(new CustomTotalResolver()));
默认情况下,AutoMapper 将源对象传递给解析器。这限制了解析器的可重用性,因为解析器与源类型耦合。但是,如果我们提供跨多种类型的通用解析器,我们配置 AutoMapper 以重定向提供给解析器的源值,并使用不同的解析器接口,以便我们的解析器可以使用源/目标成员:
1 var configuration = new MapperConfiguration(cfg => { 2 cfg.CreateMap<Source, Destination>() 3 .ForMember(dest => dest.Total, 4 opt => opt.MapFrom<CustomResolver, decimal>(src => src.SubTotal)); 5 cfg.CreateMap<OtherSource, OtherDest>() 6 .ForMember(dest => dest.OtherTotal, 7 opt => opt.MapFrom<CustomResolver, decimal>(src => src.OtherSubTotal)); 8 }); 9 10 public class CustomResolver : IMemberValueResolver<object, object, decimal, decimal> { 11 public decimal Resolve(object source, object destination, decimal sourceMember, decimal destinationMember, ResolutionContext context) { 12 // logic here 13 } 14 }
配置解析器的是给mapper传输键值对
1 //配置类里配置键值对 2 CreateMap<Source, Dest>() 3 .ForMember(dest => dest.Foo, opt => opt.MapFrom((src, dest, destMember, context) => context.Items["Foo"])); 4 5 6 7 8 9 //mapper创建的时候提取键值对 10 mapper.Map<Source, Dest>(src, opt => opt.Items["Foo"] = "Bar");
值转换器是类型转换器和值解析器之间的交叉。类型转换器是全局作用域的,因此任何时候在任何映射中从一个类型映射Foo
到另一个类型Bar
时,都会使用类型转换器。值转换器的范围为单个映射,并接收源对象和目标对象以解析为映射到目标成员的值。值转换器也可以接收源成员。
automapper支持泛型映射,看下面例子。注意:automapper支持从共有的映射列表中自动把源类转换为目标类,如果映射列表没有就需要我们自己写类型转换器,否则就会报错。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.GenericsMap 8 { 9 internal class Source<T> 10 { 11 public T Value { get; set; } 12 } 13 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.GenericsMap 8 { 9 internal class Dest<T> 10 { 11 public T Value { get; set; } 12 } 13 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.GenericsMap 9 { 10 internal class GenericsMapProfile:Profile 11 { 12 public GenericsMapProfile() 13 { 14 CreateMap(typeof(Source<>),typeof(Dest<>)); 15 ////如果映射列表没有对应的类型转换支持,需要我们自定义转换器Converter<,> 16 //CreateMap(typeof(Source<>), typeof(Dest<>)).ConvertUsing(typeof(Converter<,>)); 17 } 18 } 19 }
1 using AutoMapper; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace Demo18_AutoMapper.GenericsMap 9 { 10 internal class GenericsMapMain 11 { 12 public static void Test1() 13 { 14 var config = new MapperConfiguration(cfg => cfg.AddProfile<GenericsMapProfile>()); 15 var mapper = config.CreateMapper(); 16 //如果automapper的转换列表支持的话,automapper会自动转换,如果不支持会报错。 17 var dest = mapper.Map<Source<int>, Dest<string>>(new Source<int>() { Value = 123 }); 18 } 19 } 20 }
内置枚举器不可配置,只能替换,AutoMapper 在单独的包 AutoMapper.Extensions.EnumMapping 2.0.1 中支持基于约定的枚举值映射。对于方法CreateMap
,这个库提供了 ConvertUsingEnumMapping() 。此方法添加从源到目标枚举值的所有默认映射。可以使用 MapValue() 改变一些映射
,方法可多次使用。默认枚举值是按值 MapByValue() 映射的,也可以通过名称 MapByName() 调用映射 。
注意:①默认情况,如果两个枚举类型具有相同的值(或按名称或按值),包 AutoMapper.Extensions.EnumMapping 会将所有值从 Source 类型映射到 Dest 类型。如果启用了 EnumMappingValidation ,则所有没有 Target 等效项的 Source 枚举值都将引发异常。②如果配置了 .ReverseMap() 将支持从 Dest 类型映射到 Source 类型,注意反向映射的时候可能出现多个 Dest 值对应一个 Source 值,这会出现覆盖。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.EnumMapping 8 { 9 internal enum EnumSource 10 { 11 Default = 0, 12 First = 1, 13 Second = 2 14 } 15 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.EnumMapping 8 { 9 internal enum EnumDest 10 { 11 Default = 0, 12 Second = 2 13 } 14 }
1 using AutoMapper; 2 using AutoMapper.Extensions.EnumMapping; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace Demo18_AutoMapper.EnumMapping 10 { 11 internal class EnumMappingProfile :Profile 12 { 13 public EnumMappingProfile() 14 { 15 //需要Nuget引用AutoMapper.Extensions.EnumMapping 2.0.1 16 CreateMap<EnumSource, EnumDest>() 17 .ConvertUsingEnumMapping(opt=> opt.MapValue(EnumSource.First,EnumDest.Second)) 18 .ReverseMap(); //支持上面opt.MapValue(EnumSource.First,EnumDest.Second)的反向映射 19 } 20 } 21 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Demo18_AutoMapper.EnumMapping 8 { 9 internal class EnumMappingMain 10 { 11 public static void Test1() 12 { 13 var config = new AutoMapper.MapperConfiguration(cfg => cfg.AddProfile<EnumMappingProfile>()); 14 var mapper = config.CreateMapper(); 15 var dest = mapper.Map<EnumDest>(EnumSource.First); 16 //反向映射,这里出现EnumDest.Second会映射到EnumSource.First和EnumSource.Second, 17 //会出现覆盖现象,EnumSource.Second覆盖了EnumSource.First 18 var source =mapper.Map<EnumSource>(EnumDest.Second); 19 } 20 } 21 }
AutoMapper有个工具来验证类型映射。看下面例子:
1 public class MappingConfigurationsTests 2 { 3 [Fact] 4 public void WhenProfilesAreConfigured_ItShouldNotThrowException() 5 { 6 // Arrange 7 var config = new MapperConfiguration(configuration => 8 { 9 configuration.EnableEnumMappingValidation(); 10 11 configuration.AddMaps(typeof(AssemblyInfo).GetTypeInfo().Assembly); 12 }); 13 14 // Assert 15 config.AssertConfigurationIsValid(); 16 } 17 }
官方帮助文档 https://github.com/AutoMapper/AutoMapper/tree/master/docs
tkbSimplest的博客 https://www.cnblogs.com/farb/p/4932692.html