.Net 对象与对象之间的映射转换的6中方式以及性能对比

发布于:2024-03-29 ⋅ 阅读:(11) ⋅ 点赞:(0)

我们在.Net开发的过程中,经常会遇到一个实体对象与另一个实体对象之间的映射转换,接下来我们将依次去实现6个对象间映射转换的方式,并对他们进行性能测试,找出其中效率最高的方式。

  1. 通过对象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,
            };
        }
    }

  2. 通过反射依次找出目标实体对象中需要的属性和字段,再从源实体对象中获取到对应属性和字段的值

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

  3. 通过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));
        }
    }

  4. 通过表达式目录树解析并缓存在字典中

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

  5. 通过表示是目录树解析并应用泛型缓存

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

  6. 通过AutoMapper库实现

    var configuration = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<User, UserModel>();
    });
    configuration.AssertConfigurationIsValid();
    var mapper = configuration.CreateMapper();
    UserModel dest = mapper.Map<UserModel>(user);

  7. 性能测试,我们进行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 后查看