我们在.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序列化和反序列化,用时最短的是我们自己封装的表达式目录树用泛型缓存。