.NetCore 利用反射处理RESTful的更新

背景

在做RESTful api模式开发Api时,Patch更新时,频繁的遇到不同类型但属性相同的Dto到Model赋值的情况,

网上通用的类库是AutoMapper,但遇到了问题,查了Git也提出了问题,并未能解决。

问题

Post、Get等覆盖式传值时都能满足需求,唯独当Dto的值类型字段设为Nullable(如int?、bool?)时会被覆盖为默认值。

相关技术

Lambda表达式、反射

解决方案

 利用反射查找类的字段名称与字段类型,并将具备相同属性名的源class属性值赋值目标class,如遇到源class属为Null(引用空 或 Nullable泛型类型Null)跳过该属赋值。

  1 using System;
  2 using System.Collections.Concurrent;
  3 using System.Collections.Generic;
  4 using System.Diagnostics;
  5 using System.Linq;
  6 using System.Linq.Expressions;
  7 using System.Reflection;
  8 
  9 namespace Zhiqing.Helpers.Lambdas
 10 {
 11     /// <summary>
 12     /// 快速复制,并忽略NULL值
 13     /// </summary>
 14     public static class FastCopy
 15     {
 16         static Action<S, T> CreateCopier<S, T>()
 17         {
 18             //
 19             var source = Expression.Parameter(typeof(S));
 20             // 目标
 21             var target = Expression.Parameter(typeof(T));
 22             // 源  所有属性
 23             var props1 = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
 24             // 目标 所有属性
 25             var props2 = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
 26             // 共有属性
 27             var props = props1.Where(x => props2.Any(y => y.Name == x.Name));
 28 
 29 
 30             IEnumerable<Expression> assets = new List<Expression>();
 31 
 32             foreach (var item in props)
 33             {
 34                 // 目标
 35                 var tItem = Expression.Property(target, item.Name);
 36                 var tType = tItem.Type;
 37                 var tIsNullable = tType.IsGenericType && tType.GetGenericTypeDefinition() == typeof(Nullable<>);
 38 
 39                 //
 40                 var sItem = Expression.Property(source, item.Name);
 41                 var sType = sItem.Type;
 42                 var sIsNullable = sType.IsGenericType && sType.GetGenericTypeDefinition() == typeof(Nullable<>);
 43                 Debug.WriteLine(sIsNullable);
 44 
 45                 // ===================================
 46                 // 注释:Nullable实际是个泛型,赋值是需要转为实际类型才可赋值,否咋泛型给实际类型赋值引发异常
 47                 // 案例:int? s = 1;int t = s; 会引发异常
 48                 // 解决:int? s = 1;int t = Convert.ToInt32(s); 转换后解决
 49                 // 另外:Lamnda表达式应使用 Expression.Convert(); 转换
 50                 // 源是可为空类型
 51                 if (sIsNullable)
 52                 {
 53                     // 目标可为空
 54                     if (tIsNullable)
 55                     {
 56                         // 赋值表达式
 57                         var asset = Expression.Assign(tItem, sItem);
 58                         // 当源不为空的时候赋值
 59                         var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset);
 60                         // 加入表达式树
 61                         assets = assets.Append(notNull);
 62                     }
 63                     // 目标不可为空
 64                     else
 65                     {
 66                         // 转换源为实际类型
 67                         var sItemConverted = Expression.Convert(sItem, sType.GetGenericArguments().First());
 68                         // 赋值表达式
 69                         var asset = Expression.Assign(tItem, sItemConverted);
 70                         // 当源不为空的时候赋值
 71                         var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset);
 72                         // 加入表达式树
 73                         assets = assets.Append(notNull);
 74                     }
 75                 }
 76                 // 源不是可为空类型
 77                 else
 78                 {
 79                     // 源是否值类型
 80                     var sIsValueType = sType.IsValueType;
 81                     if (sIsValueType)
 82                     {
 83                         // 赋值表达式
 84                         var asset = Expression.Assign(tItem, sItem);
 85                         // 加入表达式树
 86                         assets = assets.Append(asset);
 87                     }
 88                     // 不是值类型
 89                     else
 90                     {
 91                         // 赋值表达式
 92                         var asset = Expression.Assign(tItem, sItem);
 93                         // 当源不为空的时候赋值
 94                         var notNull = Expression.IfThen(Expression.NotEqual(sItem, Expression.Constant(null)), asset);
 95                         // 加入表达式树
 96                         assets = assets.Append(notNull);
 97                     }
 98                 }
 99             }
100 
101             // 赋值
102             var tempBlock = Expression.Block(assets);
103             return Expression.Lambda<Action<S, T>>(tempBlock, source, target).Compile();
104         }
105 
106         static ConcurrentDictionary<string, object> actions = new ConcurrentDictionary<string, object>();
107 
108         /// <summary>
109         /// 快速的拷贝同名公共属性。忽略差异的字段。
110         /// </summary>
111         /// <typeparam name="S"></typeparam>
112         /// <typeparam name="T"></typeparam>
113         /// <param name="from"></param>
114         /// <param name="to"></param>
115         public static void Copy<S, T>(S from, T to)
116         {
117             string name = string.Format("{0}_{1}", typeof(S), typeof(T));
118 
119             if (!actions.TryGetValue(name, out object obj))
120             {
121                 var ff = CreateCopier<S, T>();
122                 actions.TryAdd(name, ff);
123                 obj = ff;
124             }
125             var act = (Action<S, T>)obj;
126             act(from, to);
127         }
128 
129 
130     }
131 }

使用案例

1 FastCopy.Copy<Card_UpdateDto, Commons.Entities.Card>(updateDto, modelCard);

猜你喜欢

转载自www.cnblogs.com/yon2068/p/9961405.html
今日推荐