方式一:自定义1
方法类:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace NetFive.UnitTest
{
/// <summary>
/// 关于对象转换已经有不少轮子(AutoMapper,TinyMapper) .出于项目需要,手动造一个简单轮子。先贴代码
/// 1.采用静态泛型类缓存,避免了拆箱装箱操作。
/// 2.对于转换对象中有,字段名一样但是类型不一样的类时仍可以用
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TTarget"></typeparam>
public static class Mapper<TSource, TTarget> where TSource : class where TTarget : class
{
public readonly static Func<TSource, TTarget> Map;
static Mapper()
{
if (Map == null)
Map = GetMap();
}
private static Func<TSource, TTarget> GetMap()
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
var parameterExpression = Expression.Parameter(sourceType, "p");
var memberInitExpression = GetExpression(parameterExpression, sourceType, targetType);
var lambda = Expression.Lambda<Func<TSource, TTarget>>(memberInitExpression, parameterExpression);
return lambda.Compile();
}
/// <summary>
/// 根据转换源和目标获取表达式树
/// </summary>
/// <param name="parameterExpression">表达式参数p</param>
/// <param name="sourceType">转换源类型</param>
/// <param name="targetType">转换目标类型</param>
/// <returns></returns>
private static MemberInitExpression GetExpression(Expression parameterExpression, Type sourceType, Type targetType)
{
var memberBindings = new List<MemberBinding>();
foreach (var targetItem in targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite))
{
var sourceItem = sourceType.GetProperty(targetItem.Name);
//判断实体的读写权限
if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
continue;
//标注NotMapped特性的属性忽略转换
if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
continue;
var propertyExpression = Expression.Property(parameterExpression, sourceItem);
//判断都是class 且类型不相同时
if (targetItem.PropertyType.IsClass && sourceItem.PropertyType.IsClass && targetItem.PropertyType != sourceItem.PropertyType)
{
if (targetItem.PropertyType != targetType)//防止出现自己引用自己无限递归
{
var memberInit = GetExpression(propertyExpression, sourceItem.PropertyType, targetItem.PropertyType);
memberBindings.Add(Expression.Bind(targetItem, memberInit));
continue;
}
}
if (targetItem.PropertyType != sourceItem.PropertyType)
continue;
memberBindings.Add(Expression.Bind(targetItem, propertyExpression));
}
return Expression.MemberInit(Expression.New(targetType), memberBindings);
}
}
}
测试类:
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace NetFive.UnitTest
{
public class AddEmployeeDto
{
public AddEmployeeDto()
{
EmpId = 1;
EmpNo = "1001";
EmpName = "网易";
Gender = Genders.男;
EntryDate = DateTime.Now;
QuitDate = null;
Outsource = true;
}
/// <summary>
/// 编号
/// </summary>
public int EmpId { get; set; }
/// <summary>
/// 工号
/// </summary>
public string EmpNo { get; set; }
/// <summary>
/// 姓名
/// </summary>
[NotMapped]//标注为 NotMapped 特性时,不转换赋值
public string EmpName { get; set; }
/// <summary>
/// 性别
/// </summary>
public Genders Gender { get; set; }
/// <summary>
/// 入职时间
/// </summary>
public DateTime EntryDate { get; set; }
/// <summary>
/// 离职时间
/// </summary>
public DateTime? QuitDate { get; set; }
/// <summary>
/// 是否外包
/// </summary>
public bool Outsource { get; set; }
public override string ToString()
{
return EmpId + "-" + EmpNo + "-" + EmpName + "-" + Gender.ToString() + EntryDate.ToString() + "-" + (QuitDate.HasValue ? QuitDate.ToString() : "") + "-" + Outsource;
}
}
}
using System;
using System.Collections.Generic;
namespace NetFive.UnitTest
{
public class EmployeeInfo
{
/// <summary>
/// 编号
/// </summary>
public int EmpId { get; set; }
/// <summary>
/// 工号
/// </summary>
public string EmpNo { get; set; }
/// <summary>
/// 姓名
/// </summary>
/// </summary>
public string EmpName { get; set; }
/// <summary>
/// 性别
/// </summary>
public Genders Gender { get; set; }
/// <summary>
/// 入职时间
/// </summary>
public DateTime EntryDate { get; set; }
/// <summary>
/// 离职时间
/// </summary>
public DateTime? QuitDate { get; set; }
/// <summary>
/// 是否外包
/// </summary>
public bool Outsource { get; set; }
public static IList<EmployeeInfo> ListEmployee(int length)
{
IList<EmployeeInfo> employeeInfos = new List<EmployeeInfo>();
EmployeeInfo entity = new EmployeeInfo();
for (int i = 1; i <= length; i++)
{
entity.EmpId = i;
entity.EmpNo = "No" + i;
entity.EmpName = "Name" + i;
entity.Gender = i % 2 == 0 ? Genders.女 : Genders.男;
entity.EntryDate = DateTime.Now.AddDays(-i);
entity.Outsource = i % 2 == 0;
employeeInfos.Add(entity);
}
return employeeInfos;
}
public override string ToString()
{
return EmpId + "-" + EmpNo + "-" + EmpName + "-" + Gender.ToString() + EntryDate.ToString() + "-" + (QuitDate.HasValue ? QuitDate.ToString() : "") + "-" + Outsource;
}
}
/// <summary>
/// 性别
/// </summary>
public enum Genders
{
女 = 0,
男 = 1
}
}
测试:
//单对象
//多对象需要循环,但查询多对象时建议使用强类型【直接输出Dto】
//参数为null时会报错,在使用方法前需要判断是否为null
AddEmployeeDto add = new AddEmployeeDto();
EmployeeInfo entity = Mapper<AddEmployeeDto, EmployeeInfo>.Map(add);
Console.WriteLine(add.ToString());
Console.WriteLine(entity.ToString());
方式2:自定义2
自定义类:
using System;
using System.Collections.Generic;
namespace XXX.Common
{
/// <summary>
/// 常用工具
/// </summary>
public class XXXUtil
{
#region 克隆对象
/// <summary>
/// 克隆对象
/// </summary>
/// <param name="target">目标</param>
/// <param name="source">数据源</param>
public static void CopyModel(object target, object source)
{
Type type1 = target.GetType();
Type type2 = source.GetType();
foreach (var mi in type2.GetProperties())
{
var des = type1.GetProperty(mi.Name);
if (des != null)
{
des.SetValue(target, mi.GetValue(source, null), null);
}
}
}
#endregion
#region 克隆对象列表
public static List<T> Clone<T, TEntity>(IList<TEntity> list) where T : new()
{
if (list.Count == 0)
{
return new List<T>();
}
List<T> items = new List<T>();
foreach (var m in list)
{
var model = new T();
var ps = model.GetType().GetProperties();
var properties = m.GetType().GetProperties();
foreach (var p in properties)
{
foreach (var pm in ps)
{
if (pm.Name == p.Name)
{
pm.SetValue(model, p.GetValue(m));
}
}
}
items.Add(model);
}
return items;
}
#endregion
}
调用方式:
单对象: EmployeeInfo addEmployeeInfo = new EmployeeInfo(); SupernodeUtil.CopyModel(addEmployeeInfo, entity); 多对象: var datas = SupernodeUtil.Clone<EmployeeShowDto, EmployeeInfo>(employeeInfos);
方式3:AutoMapper
NuGet包:[XXX.Service]
AutoMapper AutoMapper.Extensions.Microsoft.DependencyInjection
注意:左为要转换的数据类型,右边是转换后的数据类型。
WebApi项目下新建文件夹:【Custom/Mapper】

using AutoMapper;
using NetFive.Model.Entity;
using NetFive.Service.Employee.Dto;
namespace NetFive.WebApi.Custom.Mapper
{
public class AutoMapperConfigs : Profile
{
/// <summary>
/// 配置互相转换的类
/// </summary>
public AutoMapperConfigs()
{
CreateMap<AddEmployeeDto, EmployeeInfo>();
CreateMap<EmployeeInfo, EmployeeShowDto>();
}
}
}
Startup注册:

#region AutoMapper
//添加对AutoMapper的支持
services.AddAutoMapper(typeof(AutoMapperConfigs));
#endregion
调用方式:

private readonly IMapper _mapper;
public EmployeeService(IMapper mapper)
{
_mapper = mapper;
}
单对象:
EmployeeInfo entity = _mapper.Map<AddEmployeeDto, EmployeeInfo>(add);
多对象:
IList<EmployeeInfo> employeeInfos = EmployeeInfo.ListEmployee(100);
IList<EmployeeShowDto> showDtos = _mapper.Map<IList<EmployeeShowDto>>(employeeInfos);
当参数为null时:

方式4:TinyMapper【实体类用上面的】
NuGet包:
TinyMapper
调用方式:
AddEmployeeDto add = new AddEmployeeDto();
TinyMapper.Bind<AddEmployeeDto, EmployeeInfo>();
var info = TinyMapper.Map<EmployeeInfo>(add);
Console.WriteLine(info.ToString());
忽略某些字段或者绑定不同字段:
//对象名称不一样,而且需要忽略某些字段
//比如修改EmployeeInfo 的EmpName字段为Name字段
AddEmployeeDto add = new AddEmployeeDto();
TinyMapper.Bind<AddEmployeeDto, EmployeeInfo>(config =>
{
config.Ignore(x => x.EmpId);//忽略 EmpId 字段
config.Bind(x => x.EmpName, y => y.Name);//将源类型和目标类型的字段对应绑定起来
});
var info = TinyMapper.Map<EmployeeInfo>(add);
Console.WriteLine(info.ToString());
复杂类型使用:类中类,类中List数组类,使用方式和上面一样,测试无问题
为null时,会报异常:Value cannot be null. 所以使用TinyMapper需要判断是否为null
效率:来源于其他人的测试
自定义1 > AutoMapper > TinyMapper