BigBox777

爱学习、爱运动

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

阅读目录

1、介绍
2、快速入门
3、功能应用
  3.1、配置
  3.2、自定义映射属性配对
  3.3、条件映射
  3.4、自定义映射前后事件
  3.5、自定义类型转换
  3.6、自定义值解析器
  3.7、值转换器
  3.8、泛型映射
  3.9、枚举映射
4、参考

返回系列文章目录 

 

案例代码下载

 1、介绍

  AutoMapper 是一个简单的小库,基于命名约定的对象到对象的映射工具。只要2个对象的属性具有相同名字(或者符合它规定的命名约定),AutoMapper就可以替我们自动在2个对象间进行属性值的映射。如果有不符合约定的属性,或者需要自定义映射行为,就需要我们事先告诉AutoMapper,所以在使用 Map(src,dest)进行映射之前,必须使用 CreateMap() 进行配置。注意:将源映射到目标时,AutoMapper 将忽略空引用异常,可以通过自定义解析器来更改这种设置。       

 

2、快速入门

  使用之前需要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 }
源对象UserInfo
 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 }
目标对象UserInfoDto
 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 }
框架使用

 

3、功能应用

3.1、配置

  通常会新建一个配置类用来存放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 }
源Student
 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 }
目标对象StudentDto
 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 }
从程序集扫描导入配置

 

3.2、自定义映射属性配对

  上面的例子都是源类和目标类的字段属性完全一样的情况下的简单映射,本节来介绍通过配置基类  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 }
建立映射关系:property_name -> PropertyName
 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 }
去除源属性前的frm
 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 }
目标属性后加Dto
 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 }
配置编译ConfigurationCompilationProfile
 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 }
配置编译

 

 3.3、条件映射

  在满足一定条件才允许发生映射,举例:源类型是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 }
源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.ConditionalMapping
 8 {
 9     public class Dest
10     {
11         public uint Number { get; set; }
12     }
13 }
目标Dest
 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 }
配置类ConditionalMappingProfile
 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 }
案例代码

 

3.4、自定义映射前后事件

   我们可以在映射发生之前和映射发生之后定义部分事件操作。看下面的例子

 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 }
源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.BeforeAfterMapAction
 8 {
 9     internal class Dest
10     {
11         public string Number1 { get; set; }
12         public int Number2 { get; set; }
13     }
14 }
目标Dest
 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 }
配置类BeforeAfterMapActionProfile
 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 }
方案二:配置类UsingIMappingActionProfile
 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 }
方案二:映射动作类NameMappingAction
 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 }
自定义映射前后事件依赖注入

 

3.5、自定义类型转换器

  有时需要完全控制一种类型到另一种类型的转换。这通常是当一种类型看起来与另一种完全不同时,已经存在转换函数,并且您希望从“更宽松”的类型转换为更强的类型,下面例子,使用自定义了类型转换器,自定义转换器需要继承泛型接口  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 }
源类型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.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 }
目标类型Dest
 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 }
自定义转换器string=》DateTime
 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 }
自定义转换器string=》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.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 }
配置类CustomTypeConvertersProfile
 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 }
案例代码

 

3.6、自定义值解析器

   有时候目标类型的某属性需要通过源类型的多个属性进行特殊计算处理得到,这个时候就需要使用自定义解析器了。自定值解析器 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 }
源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.CustomValueResolvers
 8 {
 9     public class Dest
10     {
11         public int Total { get; set; }
12     }
13 }
目标Dest
 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 }
自定义值解析器CustomTotalResolver
 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 }
配置类CustomValueResolversProfile
 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 }
案例代码

 

上面的案例我们只向 AutoMapper 提供了自定义解析器的类型,所以映射引擎将使用反射来创建值解析器的实例。如果我们不希望 AutoMapper 使用反射来创建实例,我们可以直接提供它:
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");
View Code

 

3.7、值转换器

  值转换器是类型转换器值解析器之间的交叉类型转换器是全局作用域的,因此任何时候在任何映射中从一个类型映射Foo到另一个类型Bar时,都会使用类型转换器。值转换器的范围为单个映射,并接收源对象和目标对象以解析为映射到目标成员的值。值转换器也可以接收源成员。

 

3.8、泛型映射

  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 }
源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.GenericsMap
 8 {
 9     internal class Dest<T>
10     {
11         public T Value { get; set; }
12     }
13 }
目标Dest
 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 }
配置类GenericsMapProfile
 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 }
实例代码

 

3.9、枚举映射

   内置枚举器不可配置,只能替换,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 }
源EnumSource
 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 }
目标EnumDest
 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 }
配置类EnumMappingProfile
 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 }
验证枚举映射工具使用案例

 

4、参考

官方帮助文档  https://github.com/AutoMapper/AutoMapper/tree/master/docs  

tkbSimplest的博客 https://www.cnblogs.com/farb/p/4932692.html

posted on 2022-01-19 17:31  BigBox777  阅读(788)  评论(2编辑  收藏  举报