隐藏

C#AutoMapper使用方式和再封装

发布:2023/7/6 14:22:53作者:管理员 来源:本站 浏览次数:572



安装方式:使用vs自带的nuget管理工具,搜索AutoMapper ,选择第一个安装到你的项目即可。


我从网上找了一些资料,


参考网址:http://blog.csdn.net/csethcrm/article/details/52934325


下载了个demo,然后自己又写了一遍,我把AutoMapper 的使用分为两种:


1、viewmodel与实体的字段名字是一致的,viewmodel的字段可以与实体中的字段数量不一致。


还有一种情况是:源实体中的字段名字是Getxxx,那么viewmodel中对应的字段可以是xxx,也会自动对应赋值,比如我写的demo中源实体中GetA,viewmodel中的A;


再有一种情况就是实体中的实体赋值,在我写的这个例子中,源实体中包含的实体类字段为Sub,里面包含的字段名字为Age,


那么destmodel中对应的字段名字可以是:SubAge,那么automapper就可以自动为你赋值了,大家看最后的运行结果。


给大家看下我建的源实体:



   public class Source1

   {

       public string Name { set; get; }


       public string GetA { set; get; }


       public string GetD { set; get; }


       public string SetB { set; get; }


       public string c { set; get; }


       public SubSource1 Sub { set; get; }

   }


   public class SubSource1

   {

       public string Age { set; get; }

   }




还有viewmodel(要转化成为你想要的模型):



   public class Dest1

   {

       public string Name { set; get; }


       public string A { set; get; }


       public string C { set; get; }


       public string SubAge { set; get; }


       public string D { set; get; }

   }




我封装的扩展方法:





       /// <summary>

       ///  类型映射,默认字段名字一一对应

       /// </summary>

       /// <typeparam name="TDestination">转化之后的model,可以理解为viewmodel</typeparam>

       /// <typeparam name="TSource">要被转化的实体,Entity</typeparam>

       /// <param name="source">可以使用这个扩展方法的类型,任何引用类型</param>

       /// <returns>转化之后的实体</returns>

       public static TDestination MapTo<TDestination, TSource>(this TSource source)

           where TDestination : class

           where TSource : class

       {

           if (source == null) return default(TDestination);

           var config = new MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());

           var mapper = config.CreateMapper();

           return mapper.Map<TDestination>(source);

       }




 


使用方式:



               var source1 = new Source1

               {

                   Name = "source",

                   Sub = new SubSource1 { Age = "25" },

                   c = "c",

                   GetA = "A",

                   SetB = "B"

               };


               var destViewModel = source1.MapTo<Source1,Dest1>();




运行结果:






2.viewmodel与实体字段名字没有全部对应,只有几个字段的名字和源实体中的字段名字是一样的,其他的字段是通过实体中的几个字段组合或者是格式或者是类型转化而来的,


使用方法:不能再使用这个扩展方法了,只能自己额外写代码,代码如下:



              var config2 = new MapperConfiguration(

                   cfg => cfg.CreateMap<SourceUser, DestUser2>()

                       .ForMember(d => d.DestName, opt => opt.MapFrom(s => s.Name))    //指定字段一一对应

                       .ForMember(d => d.Birthday, opt => opt.MapFrom(src => src.Birthday.ToString("yy-MM-dd HH:mm")))//指定字段,并转化指定的格式

                       .ForMember(d => d.Age, opt => opt.Condition(src => src.Age > 5))//条件赋值

                       .ForMember(d => d.A1, opt => opt.Ignore())//忽略该字段,不给该字段赋值

                       .ForMember(d => d.A1, opt => opt.NullSubstitute("Default Value"))//如果源字段值为空,则赋值为 Default Value

                       .ForMember(d => d.A1, opt => opt.MapFrom(src => src.Name + src.Age * 3 + src.Birthday.ToString("d"))));//可以自己随意组合赋值

               var mapper2 = config2.CreateMapper();




注释中都包含了平时常用的几种情况,其他的我就没有再写。


下面再给大家把list转化的扩展方法代码贴上:





       /// <summary>

       /// 集合列表类型映射,默认字段名字一一对应

       /// </summary>

       /// <typeparam name="TDestination">转化之后的model,可以理解为viewmodel</typeparam>

       /// <typeparam name="TSource">要被转化的实体,Entity</typeparam>

       /// <param name="source">可以使用这个扩展方法的类型,任何引用类型</param>

       /// <returns>转化之后的实体列表</returns>

       public static IEnumerable<TDestination> MapToList<TSource,TDestination>(this IEnumerable<TSource> source)

           where TDestination : class

           where TSource : class

       {

           if (source == null) return new List<TDestination>();

           var config = new MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());

           var mapper = config.CreateMapper();

           return mapper.Map<List<TDestination>>(source);

       }




 


同样的使用方式:



               var source1 = new Source1

               {

                   Name = "source",

                   Sub = new SubSource1 { Age = "25" },

                   c = "c",

                   GetA = "A",

                   SetB = "B"

               };

var source3 = new Source1

               {

                   Name = "source3",

                   Sub = new SubSource1 { Age = "253" },

                   c = "c3",

                   GetA = "A3",

                   SetB = "B3"

               };

               var sourceList = new List<Source1> { source1, source3 };

               var destViewModelList = sourceList.MapToList<Source1,Dest1>();




运行结果:






以上就是我个人所得,如有错误,欢迎大家指正。




//2017.12.4 修改:destination和source写反了,导致我的总结有些错误,现在纠正一下:错误结论已经红色标注,中间的截图也换成正确的了,工具类方法也已经修正。




出处:https://www.cnblogs.com/dawenyang/p/7966850.html


===========================================================

一、最简单的用法


有两个类User和UserDto

复制代码

复制代码


1     public class User

2     {

3         public int Id { get; set; }

4         public string Name { get; set; }

5         public int Age { get; set; }

6     }

7

8     public class UserDto

9     {

10         public string Name { get; set; }

11         public int Age { get; set; }

12     }




将User转换成UserDto也和简单



1     Mapper.Initialize(x => x.CreateMap<User, UserDto>());

2     User user = new User()

3     {

4         Id = 1,

5         Name = "caoyc",

6         Age = 20

7     };

8     var dto = Mapper.Map<UserDto>(user);




这是一种最简单的使用,AutoMapper会更加字段名称去自动对于,忽略大小写。



二、如果属性名称不同


将UserDto的Name属性改成Name2



1     Mapper.Initialize(x =>

2         x.CreateMap<User, UserDto>()

3          .ForMember(d =>d.Name2, opt => {

4             opt.MapFrom(s => s.Name);

5             })

6         );

7

8     User user = new User()

9     {

10         Id = 1,

11         Name = "caoyc",

12         Age = 20

13     };

14

15     var dto = Mapper.Map<UserDto>(user);





三、使用Profile配置


自定义一个UserProfile类继承Profile,并重写Configure方法







1     public class UserProfile : Profile

2     {

3         protected override void Configure()

4         {

5             CreateMap<User, UserDto>()

6                 .ForMember(d => d.Name2, opt =>

7                 {

8                     opt.MapFrom(s => s.Name);

9                 });

10         }

11     }






新版本的 autoMapper.UserProfile.Configure()”: 可能会有提示:没有找到适合的方法来重写 。


可以改为构造函数注入的方式

复制代码


   public class UserProfile : Profile

   {

       public UserProfile ()

       {

           CreateMap<User, UserDto>()

               .ForMember(d => d.Name2, opt =>

               {

                   opt.MapFrom(s => s.Name);

               });

       }

   }






使用时就这样



1     Mapper.Initialize(x => x.AddProfile<UserProfile>());

2

3     User user = new User()

4     {

5         Id = 1,

6         Name = "caoyc",

7         Age = 20

8     };

9

10     var dto = Mapper.Map<UserDto>(user);





四、空值替换NullSubstitute


空值替换允许我们将Source对象中的空值在转换为Destination的值的时候,使用指定的值来替换空值。



1     public class UserProfile : Profile

2     {

3         protected override void Configure()

4         {

5             CreateMap<User, UserDto>()

6                 .ForMember(d => d.Name2, opt => opt.MapFrom(s => s.Name))

7                 .ForMember(d => d.Name2, opt => opt.NullSubstitute("值为空"));

8                

9         }

10     }




1     Mapper.Initialize(x => x.AddProfile<UserProfile>());

2

3     User user = new User()

4     {

5         Id = 1,

6         Age = 20

7     };

8

9     var dto = Mapper.Map<UserDto>(user);




结果为:



五、忽略属性Ignore



1     public class User

2     {

3         public int Id { get; set; }

4         public string Name { get; set; }

5         public int Age { get; set; }

6     }

7

8     public class UserDto

9     {

10         public string Name { get; set; }

11         public int Age { get; set; }

12

13     }

14

15     public class UserProfile : Profile

16     {

17         protected override void Configure()

18         {

19             CreateMap<User, UserDto>().ForMember("Name", opt => opt.Ignore());

20         }

21     }




使用



1     Mapper.Initialize(x => x.AddProfile<UserProfile>());

2

3     User user = new User()

4     {

5         Id = 1,

6         Name="caoyc",

7         Age = 20

8     };

9

10     var dto = Mapper.Map<UserDto>(user);




结果:



六、预设值


如果目标属性多于源属性,可以进行预设值



1     public class User

2     {

3         public int Id { get; set; }

4         public string Name { get; set; }

5         public int Age { get; set; }

6     }

7

8     public class UserDto

9     {

10         public string Name { get; set; }

11         public int Age { get; set; }

12         public string Gender { get; set; }

13

14     }

15

16     public class UserProfile : Profile

17     {

18         protected override void Configure()

19         {

20             CreateMap<User, UserDto>();

21         }

22     }




使用



1     Mapper.Initialize(x => x.AddProfile<UserProfile>());

2

3     User user = new User()

4     {

5         Id = 1,

6         Name="caoyc",

7         Age = 20

8     };

9

10     UserDto dto = new UserDto() {Gender = "男"};

11     Mapper.Map(user, dto);




七、类型转换ITypeConverter


如果数据中Gender存储的int类型,而DTO中Gender是String类型



1     public class User

2     {

3         public int Gender { get; set; }

4     }

5

6     public class UserDto

7     {

8         public string Gender { get; set; }

9     }




类型转换类,需要实现接口ITypeConverter



1     public class GenderTypeConvertert : ITypeConverter<int, string>

2     {

3         public string Convert(int source, string destination, ResolutionContext context)

4         {

5             switch (source)

6             {

7                 case 0:

8                     destination = "男";

9                     break;

10                 case 1:

11                     destination = "女";

12                     break;

13                 default:

14                     destination = "未知";

15                     break;

16             }

17             return destination;

18         }

19     }




配置规则



1     public class UserProfile : Profile

2     {

3         protected override void Configure()

4         {

5             CreateMap<User, UserDto>();

6

7             CreateMap<int, string>().ConvertUsing<GenderTypeConvertert>();

8             //也可以写这样

9             //CreateMap<int, string>().ConvertUsing(new GenderTypeConvertert());

10         }

11     }




使用



1     Mapper.Initialize(x => x.AddProfile<UserProfile>());

2

3     User user0 = new User() { Gender = 0 };

4     User user1 = new User() { Gender = 1 };

5     User user2 = new User() { Gender = 2 };

6     var dto0= Mapper.Map<UserDto>(user0);

7     var dto1 = Mapper.Map<UserDto>(user1);

8     var dto2 = Mapper.Map<UserDto>(user2);

9

10     Console.WriteLine("dto0:{0}", dto0.Gender);

11     Console.WriteLine("dto1:{0}", dto1.Gender);

12     Console.WriteLine("dto2:{0}", dto2.Gender);




结果




八、条件约束Condition


当满足条件时才进行映射字段,例如人类年龄,假设我们现在人类年龄范围为0-200岁(这只是假设),只有满足在这个条件才进行映射


DTO和Entity



1     public class User

2     {

3         public int Age { get; set; }

4     }

5

6     public class UserDto

7     {

8         public int Age { get; set; }

9     }




Profile



1     public class UserProfile : Profile

2     {

3         protected override void Configure()

4         {

5             CreateMap<User, UserDto>().ForMember(dest=>dest.Age,opt=>opt.Condition(src=>src.Age>=0 && src.Age<=200));

6         }

7     }




使用代码



1     Mapper.Initialize(x => x.AddProfile<UserProfile>());

2

3     User user0 = new User() { Age = 1 };

4     User user1 = new User() { Age = 150 };

5     User user2 = new User() { Age = 201 };

6     var dto0= Mapper.Map<UserDto>(user0);

7     var dto1 = Mapper.Map<UserDto>(user1);

8     var dto2 = Mapper.Map<UserDto>(user2);

9

10     Console.WriteLine("dto0:{0}", dto0.Age);

11     Console.WriteLine("dto1:{0}", dto1.Age);

12     Console.WriteLine("dto2:{0}", dto2.Age);




输出结果






出处:https://www.cnblogs.com/caoyc/p/6367828.html


===================================================

AutoMapper介绍


为什么要使用AutoMapper?


我们在实现两个实体之间的转换,首先想到的就是新的一个对象,这个实体的字段等于另一个实体的字段,这样确实能够实现两个实体之间的转换,但这种方式的扩展性,灵活性非常差,维护起来相当麻烦;实体之前转换的工具有很多,不过我还是决定使用AutoMapper,因为它足够轻量级,而且也非常流行,国外的大牛们都使用它使用AutoMapper可以很方便的实现实体和实体之间的转换,它是一个强大的对象映射工具。


一,如何添加AutoMapper到项目中?


在vs中使用打开工具 - 库程序包管理器 - 程序包管理控制平台,输入“Install-Package AutoMapper”命令,就可以把AutoMapper添加到项目中了〜


二,举个栗子


栗子1:两个实体之间的映射


Mapper.CreateMap <Test1,Test2>(); Test1 test1 = new Test1 {Id = 1,Name =“张三”,Date = DateTime.Now}; Test2 test2 = Mapper.Map <Test2>(test1);




栗子2:两个实体不同字段之间的映射




Mapper.CreateMap <Test1,Test2>()。ForMember(d => d.Name121,opt => opt.MapFrom(s => s.Name));




栗子3:泛型之间的映射


   

     Mapper.CreateMap <Test1,Test2>();

   

               var testList = Mapper.Map <List <Test1>,List <Test2 >>(testList);


三,扩展映射方法使映射变得更简单




   

   using System.Collections;

   

   using System.Collections.Generic;

   

   using System.Data;

   

   using AutoMapper;

   

   namespace Infrastructure.Utility

   

   

   

   {

   

   /// <summary>

   

   /// AutoMapper扩展帮助类

   

   /// </summary>

   

   public static class AutoMapperHelper

   

   {

   

   /// <summary>

   

   /// 类型映射

   

   /// </summary>

   

   public static T MapTo<T>(this object obj)

   

   {

   

   if (obj == null) return default(T);

   

   Mapper.CreateMap(obj.GetType(), typeof(T));

   

   return Mapper.Map<T>(obj);

   

   }

   

   /// <summary>

   

   /// 集合列表类型映射

   

   /// </summary>

   

   public static List<TDestination> MapToList<TDestination>(this IEnumerable source)

   

   {

   

   foreach (var first in source)

   

   {

   

   var type = first.GetType();

   

   Mapper.CreateMap(type, typeof(TDestination));

   

   break;

   

   }

   

   return Mapper.Map<List<TDestination>>(source);

   

   }

   

   /// <summary>

   

   /// 集合列表类型映射

   

   /// </summary>

   

   public static List<TDestination> MapToList<TSource, TDestination>(this IEnumerable<TSource> source)

   

   {

   

   //IEnumerable<T> 类型需要创建元素的映射

   

   Mapper.CreateMap<TSource, TDestination>();

   

   return Mapper.Map<List<TDestination>>(source);

   

   }

   

   /// <summary>

   

   /// 类型映射

   

   /// </summary>

   

   public static TDestination MapTo<TSource, TDestination>(this TSource source, TDestination destination)

   

   where TSource : class

   

   where TDestination : class

   

   {

   

   if (source == null) return destination;

   

   Mapper.CreateMap<TSource, TDestination>();

   

   return Mapper.Map(source, destination);

   

   }

   

   /// <summary>

   

   /// DataReader映射

   

   /// </summary>

   

   public static IEnumerable<T> DataReaderMapTo<T>(this IDataReader reader)

   

   {

   

   Mapper.Reset();

   

   Mapper.CreateMap<IDataReader, IEnumerable<T>>();

   

   return Mapper.Map<IDataReader, IEnumerable<T>>(reader);

   

   }

   

   }

   

   }


这样的话,你就可以这样使用了


   var testDto = test.MapTo <Test2>();


   var testDtoList = testList.MapTo <Test2>();








出处:https://blog.csdn.net/qq_35193189/article/details/80805451


=========================================================

构造函数映射:

Automapper – 如何映射到构造函数参数,而不是属性设置


我是使用的AutoMapper的版本是9.0


使用ConstructUsing


这将允许您指定在映射期间使用的构造函数。但是所有其他属性将根据约定自动映射。


注意,这不同于ConvertUsing,因为convert使用将不会继续通过约定映射,它会让你完全控制映射。


Mapper.CreateMap<ObjectFrom, ObjectTo>()

   .ConstructUsing(x => new ObjectTo(x.arg0, x.arg1, x.etc));


具体使用,参考如下代码:



复制代码


namespace AutoMapperTest

{

   class Program

   {

       static void Main(string[] args)

       {

           var config = new MapperConfiguration(cif => cif.AddProfile <UserProfile>()); //方式一

           //var config = new MapperConfiguration(cif => cif.AddProfile(new UserProfile()));//方式二

           var mapper = config.CreateMapper();


           var f = new ObjectFrom { TestName = "aaa", TestAge = 12, TestSex = "m" };

           Console.WriteLine(JsonConvert.SerializeObject(f) + Environment.NewLine);


           var t = mapper.Map<ObjectTo>(f);

           Console.WriteLine(JsonConvert.SerializeObject(t) + Environment.NewLine);


           Console.ReadKey();

       }

   }


   public class ObjectFrom

   {

       public string TestName { get; set; }

       public int TestAge { get; set; }

       public string TestSex { get; set; }

   }


   public class ObjectTo

   {

       public ObjectTo(string name)

       {

           if (name == null)

           {

               throw new InvalidDataException("name cannot be null");

           }

           else

           {

               this._name = name;

           }

       }


       private readonly string _name;

       public string Name { get { return _name; } }

       public int Age { get; set; }

       public string Gender { get; set; }


   }


}


复制代码




参考出处:http://www.voidcn.com/article/p-swatacoc-bsk.html


=========================================================


根据上面的理解和站在巨人的肩膀上,自己重新封装一次,用到项目中。

复制代码


   public static class AutoMapHelper

   {


       /// <summary>

       ///  类型映射,默认字段名字一一对应

       /// </summary>

       /// <typeparam name="TDestination"></typeparam>

       /// <param name="obj"></param>

       /// <returns></returns>

       public static TDestination AutoMapTo<TDestination>(this object obj)

       {

           if (obj == null) return default(TDestination);

           var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap(obj.GetType(), typeof(TDestination)));

           return config.CreateMapper().Map<TDestination>(obj);

       }


       /// <summary>

       /// 类型映射,可指定映射字段的配置信息

       /// </summary>

       /// <typeparam name="TSource">源数据:要被转化的实体对象</typeparam>

       /// <typeparam name="TDestination">目标数据:转换后的实体对象</typeparam>

       /// <param name="source">任何引用类型对象</param>

       /// <param name="cfgExp">可为null,则自动一一映射</param>

       /// <returns></returns>

       public static TDestination AutoMapTo<TSource, TDestination>(this TSource source, Action<AutoMapper.IMapperConfigurationExpression> cfgExp)

        where TDestination : class

        where TSource : class

       {

           if (source == null) return default(TDestination);

           var config = new AutoMapper.MapperConfiguration(cfgExp != null ? cfgExp : cfg => cfg.CreateMap<TSource, TDestination>());

           var mapper = config.CreateMapper();

           return mapper.Map<TDestination>(source);

       }




       /// <summary>

       /// 类型映射,默认字段名字一一对应

       /// </summary>

       /// <typeparam name="TSource">源数据:要被转化的实体对象</typeparam>

       /// <typeparam name="TDestination">目标数据:转换后的实体对象</typeparam>

       /// <param name="source">任何引用类型对象</param>

       /// <returns>转化之后的实体</returns>

       public static TDestination AutoMapTo<TSource, TDestination>(this TSource source)

           where TDestination : class

           where TSource : class

       {

           if (source == null) return default(TDestination);

           var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());

           var mapper = config.CreateMapper();

           return mapper.Map<TDestination>(source);

       }



       /// <summary>

       /// 集合列表类型映射,默认字段名字一一对应

       /// </summary>

       /// <typeparam name="TDestination">转化之后的实体对象,可以理解为viewmodel</typeparam>

       /// <typeparam name="TSource">要被转化的实体对象,Entity</typeparam>

       /// <param name="source">通过泛型指定的这个扩展方法的类型,理论任何引用类型</param>

       /// <returns>转化之后的实体列表</returns>

       public static IEnumerable<TDestination> AutoMapTo<TSource, TDestination>(this IEnumerable<TSource> source)

           where TDestination : class

           where TSource : class

       {

           if (source == null) return new List<TDestination>();

           var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());

           var mapper = config.CreateMapper();

           return mapper.Map<List<TDestination>>(source);

       }




   }


复制代码


其实还可以继续优化,在泛型中不需要知道指定source的类型了,因为可以直接获取到,后面有时间再调整吧。