# DTO数据传输对象

简单来说Model面向业务,我们是通过业务来定义Model的。而DTO是面向界面UI,是通过UI的需求来定义的。通过DTO我们实现了表现层与Model之间的解耦,表现层不引用Model,如果开发过程中我们的模型改变了,而界面没变,我们就只需要改Model而不需要去改表现层中的东西。

public class User
{
	 public int Id { get; set; }
	 public string Name { get; set; }
	 public int Age { get; set; }
 }

 public class UserDto
 {
	 public string Name { get; set; }
	 public int Age { get; set; }
 }

# 最简单的用法

AutoMapper会根据字段名称去自动对于,忽略大小写。

Mapper.Initialize(x => x.CreateMap<User, UserDto>());
 User user = new User()
  {
      Id = 1,
       Name = "caoyc",
      Age = 20
   };
var dto = Mapper.Map<UserDto>(user);

# 如果属性名称不同

Mapper.Initialize(x => 
     x.CreateMap<User, UserDto>()
      .ForMember(d =>d.Name2, opt => {
         opt.MapFrom(s => s.Name);
          })
    );
   User user = new User()
   {
      Id = 1,
      Name = "caoyc",
      Age = 20
   };
 var dto = Mapper.Map<UserDto>(user);

# 使用Profile配置

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

public class UserProfile : Profile
  {
    protected override void Configure()
      {
         CreateMap<User, UserDto>().ForMember(d => d.Name2, opt =>
            {
                opt.MapFrom(s => s.Name);
           });
       }
}

使用时就这样

Mapper.Initialize(x => x.AddProfile<UserProfile>());
   User user = new User()
  {
     Id = 1,
     Name = "caoyc",
     Age = 20
  };
var dto = Mapper.Map<UserDto>(user);

# 空值替换NullSubstitute

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

public class UserProfile : Profile
 {
     protected override void Configure()
     {
		   CreateMap<User, UserDto>()
			 .ForMember(d => d.Name2, opt => opt.MapFrom(s => s.Name))
			 .ForMember(d => d.Name2, opt => opt.NullSubstitute("值为空"));
     }
 }

# 忽略属性Ignore

public class UserProfile : Profile
{
	protected override void Configure()
	{
		 CreateMap<User, UserDto>().ForMember("Name", opt => opt.Ignore());
	} 
}

# 预设值

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

Mapper.Initialize(x => x.AddProfile<UserProfile>());
 User user = new User()
{
      Id = 1,
      Name="caoyc",
      Age = 20
};
UserDto dto = new UserDto() {Gender = "男"};
Mapper.Map(user, dto);

# 类型转换ITypeConverter

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

public class User
{
    public int Gender { get; set; }
 }
public class UserDto
{
    public string Gender { get; set; }
}

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

public class GenderTypeConvertert : ITypeConverter<int, string>
{
  public string Convert(int source, string destination, ResolutionContext context)
  {
	  switch (source)
	  {
		  case 0:
			   destination = "男";
			   break;
		  case 1:
			   destination = "女";
			   break;
		  default:
			   destination = "未知";
			   break;
	   }
	   return destination;
  }
}

配置规则

public class UserProfile : Profile
{
     protected override void Configure()
     {
		CreateMap<User, UserDto>();
		CreateMap<int, string>().ConvertUsing<GenderTypeConvertert>();
		//也可以写这样
		//CreateMap<int, string>().ConvertUsing(new GenderTypeConvertert());
     }
}

使用

Mapper.Initialize(x => x.AddProfile<UserProfile>());
User user0 = new User() { Gender = 0 };
User user1 = new User() { Gender = 1 };
User user2 = new User() { Gender = 2 };
var dto0= Mapper.Map<UserDto>(user0);
var dto1 = Mapper.Map<UserDto>(user1);
var dto2 = Mapper.Map<UserDto>(user2);

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

# 条件约束Condition

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

public class User
{
    public int Age { get; set; }
}

public class UserDto
{
   public int Age { get; set; }
}
public class UserProfile : Profile
{
     protected override void Configure()
     {
          CreateMap<User, UserDto>()
		  .ForMember(dest=>dest.Age,opt=>opt.Condition(src=>src.Age<=100));
     }
}

使用代码

Mapper.Initialize(x => x.AddProfile<UserProfile>());
User user0 = new User() { Age = 1 };
User user1 = new User() { Age = 150 };
User user2 = new User() { Age = 201 };
var dto0= Mapper.Map<UserDto>(user0);
var dto1 = Mapper.Map<UserDto>(user1);
var dto2 = Mapper.Map<UserDto>(user2);

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

# AutoMapperHelper

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);
        }
    }
}

你可以像下面的栗子这样使用:

//对象映射
ShipInfoModel  shipInfoModel =  ShipInfo.MapTo<ShipInfoModel>();
//列表映射
List< ShipInfoModel > shipInfoModellist = ShipInfoList.MapToList<ShipInfoModel>();

# NET CORE 使用AutoMapper

# 安装依赖包

  • AutoMapper
  • AutoMapper.Extensions.Microsoft.Dependencyinjection

# 注册服务

services.AddAutoMapper(typeof(AutoMapperConfiguration));

# 创建映射规则

public class AutoMapperConfiguration:Profile{
	public AutoMapperConfiguration(){
		CreateMap<GoodsEntity,GoodsDto>()
		//映射发生之前统一处理 操作 src
		.BeforeMap((src,dest)=>src.Price=src.Price+10)
		.BeforeMap((src,dest)=>src.CreatTime=src.CreatTime==null?DateTime.Now:src.CreatTime)
		
		//映射匹配
		.ForMember(dest=>dest.GoodsName,opt=>opt.MapFrom(src=>src.Name))
		.ForMember(dest=>dest.CreatTime,opt=>opt.MapFrom(src=>src.CreatTime.ToString("yyyy-MM-dd")))
		//匹配过程中赋值
		.ForMember(dest=>dest.Price,opt=>opt.MapFrom(src=>src.Price+10))
		//忽略某个属性的映射
		.ForMemeber(dest>dest.IsDeleted,opt=>opt.Ignore())
		//合并
		.ForMemeber(dest>dest.GoodsName,opt=>opt.MapFrom(src=>src.Name+src.Brands.Name))
		
		//映射发生之后 操作 dest
	    .AfterMap((src,dest)=>src.GoodsName=dest.Price<40?"":dest.GoodsName));
		
		//最简单的匹配,属性字段、类型完全一致
		//实体转DTO
		CreateMap<GoodsEntity,GoodsDto>();
	}
}

# 使用

public class TestController:ControllerBase{
	private readonly IMapper _mapper;
	public TestController(IMapper mapper){
		_mapper=mapper;
	}
	
	public async Task<List<string>> GetList(){
		var goodsDtos=_mapper.Map<List<GoodsDto>>(allGoodsList);
		return new List<string>{goodsDtos};
	}
}