我们在.Net开发的过程中,经常会遇到一个实体对象与另一个实体对象之间的映射转换,接下来我们将依次去实现6个对象间映射转换的方式,并对他们进行性能测试,找出其中效率最高的方式。
通过对象Copy,通过new一个新的实体对象通过手动赋值的方式实现
public class ObjectCopyMapper { /// <summary> /// 第一种就是通过New一个目标对象给所有属性手动赋值 /// </summary> public static UserModel Trans(User userEntry) { return new UserModel() { Id = userEntry.Id, UserName = userEntry.UserName, NickName = userEntry.NickName, }; } }
通过反射依次找出目标实体对象中需要的属性和字段,再从源实体对象中获取到对应属性和字段的值
public class ReflectionMapper { /// <summary> /// 通过反射的方式实现对象之间的映射 /// </summary> /// <typeparam name="TIn"></typeparam> /// <typeparam name="TOut"></typeparam> /// <param name="tIn"></param> /// <returns></returns> public static TOut Trans<TIn, TOut>(TIn tIn) { TOut tOut = Activator.CreateInstance<TOut>(); foreach (var itemOut in tOut.GetType().GetProperties()) { var propIn = tIn.GetType().GetProperty(itemOut.Name); itemOut.SetValue(tOut, propIn.GetValue(tIn)); } foreach (var itemOut in tOut.GetType().GetFields()) { var fieldIn = tIn.GetType().GetField(itemOut.Name); itemOut.SetValue(tOut, fieldIn.GetValue(tIn)); } return tOut; } }
通过Json序列化反序列化的方式实现,这种是最简单的
public class SerializeMapper { /// <summary> /// 通过json序列化反序列化的方式实现 /// </summary> /// <typeparam name="TIn"></typeparam> /// <typeparam name="TOut"></typeparam> /// <param name="tIn"></param> /// <returns></returns> public static TOut Trans<TIn, TOut>(TIn tIn) { return JsonConvert.DeserializeObject<TOut>(JsonConvert.SerializeObject(tIn)); } }
通过表达式目录树解析并缓存在字典中
public class ExpressionMapper { private static Dictionary<string, object> dics = new Dictionary<string, object>(); /// <summary> /// 采用字典缓存表达式树实现 /// </summary> /// <typeparam name="TIn"></typeparam> /// <typeparam name="TOut"></typeparam> /// <param name="tIn"></param> /// <returns></returns> public static TOut Trans<TIn, TOut>(TIn tIn) { string key = $"funckey_{typeof(TIn).FullName}_{typeof(TOut).FullName}"; if (!dics.ContainsKey(key)) { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List<MemberBinding> memberBindings = new List<MemberBinding>(); foreach(var item in typeof(TOut).GetProperties()) { MemberExpression memberExpression = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, memberExpression); memberBindings.Add(memberBinding); } foreach (var item in typeof(TOut).GetFields()) { MemberExpression memberExpression = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name)); MemberBinding memberBinding = Expression.Bind(item, memberExpression); memberBindings.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindings.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); Func<TIn, TOut> func = lambda.Compile();//只拼装一次 dics[key] = func; } return ((Func<TIn, TOut>)dics[key]).Invoke(tIn); } }
通过表示是目录树解析并应用泛型缓存
/// <summary> /// 采用泛型缓存表达式目录树实现 /// </summary> /// <typeparam name="TIn"></typeparam> /// <typeparam name="TOut"></typeparam> public class ExpressionGenericMapper<TIn, TOut> { private static Func<TIn, TOut> _FUNC = null; static ExpressionGenericMapper() { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List<MemberBinding> memberBindingList = new List<MemberBinding>(); foreach (var item in typeof(TOut).GetProperties()) { MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } foreach (var item in typeof(TOut).GetFields()) { MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); _FUNC = lambda.Compile();//拼装是一次性的 } public static TOut Trans(TIn t) { return _FUNC(t); } }
通过AutoMapper库实现
var configuration = new MapperConfiguration(cfg => { cfg.CreateMap<User, UserModel>(); }); configuration.AssertConfigurationIsValid(); var mapper = configuration.CreateMapper(); UserModel dest = mapper.Map<UserModel>(user);
性能测试,我们进行100W次映射转换,统计各种方式需要的执行时间
User user = new User() { Id = 1, UserName = "Test", NickName = "测试用户", }; long commonTime = 0; Stopwatch watch = new Stopwatch(); watch.Start(); { for (int i = 0; i < 1_000_000; i++) { ObjectCopyMapper.Trans(user); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; Console.WriteLine($"ObjectCopyMapper 用时:{commonTime}ms"); } { watch.Restart(); for (int i = 0; i < 1_000_000; i++) { ReflectionMapper.Trans<User, UserModel>(user); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; Console.WriteLine($"ReflectionMapper 用时:{commonTime}ms"); } { watch.Restart(); for (int i = 0; i < 1_000_000; i++) { SerializeMapper.Trans<User, UserModel>(user); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; Console.WriteLine($"SerializeMapper 用时:{commonTime}ms"); } { watch.Restart(); for (int i = 0; i < 1_000_000; i++) { ExpressionMapper.Trans<User, UserModel>(user); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; Console.WriteLine($"ExpressionMapper 用时:{commonTime}ms"); } { watch.Restart(); for (int i = 0; i < 1_000_000; i++) { UserModel model = ExpressionGenericMapper<User, UserModel>.Trans(user); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; Console.WriteLine($"ExpressionGenericMapper 用时:{commonTime}ms"); } { watch.Restart(); var configuration = new MapperConfiguration(cfg => { cfg.CreateMap<User, UserModel>(); }); configuration.AssertConfigurationIsValid(); var mapper = configuration.CreateMapper(); for (int i = 0; i < 1_000_000; i++) { UserModel dest = mapper.Map<UserModel>(user); } watch.Stop(); commonTime = watch.ElapsedMilliseconds; Console.WriteLine($"AutoMapper 用时:{commonTime}ms"); }
测试结果:
ObjectCopyMapper 用时:52ms
ReflectionMapper 用时:892ms
SerializeMapper 用时:3468ms
ExpressionMapper 用时:203ms
ExpressionGenericMapper 用时:35ms
AutoMapper 用时:209ms
根据上面的测试数据,可以发现用时最长的是我们Json序列化和反序列化,用时最短的是我们自己封装的表达式目录树用泛型缓存。
本文含有隐藏内容,请 开通VIP 后查看