ExpressionGenericMapper.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel.DataAnnotations.Schema;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Reflection;
  8. namespace Masuit.Tools.Mapping
  9. {
  10. /// <summary>
  11. /// 表达式树实体映射器
  12. /// </summary>
  13. /// <typeparam name="TSource"></typeparam>
  14. /// <typeparam name="TDest"></typeparam>
  15. public static class ExpressionGenericMapper<TSource, TDest> where TSource : class where TDest : class
  16. {
  17. // 缓存委托
  18. private static Func<TSource, TDest> MapFunc;
  19. private static Action<TSource, TDest> MapAction;
  20. private static Func<TSource, TDest> _copyFunc;
  21. private static Action<TSource, TDest> _copyAction;
  22. /// <summary>
  23. /// 对象映射
  24. /// </summary>
  25. /// <param name="source"></param>
  26. /// <returns></returns>
  27. public static TDest Map(TSource source)
  28. {
  29. if (MapFunc == null)
  30. {
  31. MapFunc = GetMapFunc();
  32. }
  33. return MapFunc(source);
  34. }
  35. /// <summary>
  36. /// 集合映射
  37. /// </summary>
  38. /// <param name="sources"></param>
  39. /// <returns></returns>
  40. public static List<TDest> MapList(IEnumerable<TSource> sources)
  41. {
  42. if (MapFunc == null)
  43. {
  44. MapFunc = GetMapFunc();
  45. }
  46. return sources.Select(MapFunc).ToList();
  47. }
  48. /// <summary>
  49. /// 映射到已经实例化的对象
  50. /// </summary>
  51. /// <param name="source"></param>
  52. /// <param name="target"></param>
  53. public static void Map(TSource source, TDest target)
  54. {
  55. if (MapAction == null)
  56. {
  57. MapAction = GetMapAction();
  58. }
  59. MapAction(source, target);
  60. }
  61. private static Func<TSource, TDest> GetMapFunc()
  62. {
  63. var sourceType = typeof(TSource);
  64. var targetType = typeof(TDest);
  65. if (MapperTools.IsEnumerable(sourceType) || MapperTools.IsEnumerable(targetType))
  66. {
  67. throw new NotSupportedException("数组类型暂不支持对象映射,请使用List类型");
  68. }
  69. //Func委托传入变量
  70. var parameter = Expression.Parameter(sourceType, "p");
  71. var memberBindings = new List<MemberBinding>();
  72. var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite);
  73. foreach (var targetItem in targetTypes)
  74. {
  75. var sourceItem = sourceType.GetProperty(targetItem.Name);
  76. //判断对象的读写权限
  77. if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
  78. {
  79. continue;
  80. }
  81. //标注NotMapped特性的属性忽略转换
  82. if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
  83. {
  84. continue;
  85. }
  86. var sourceProperty = Expression.Property(parameter, sourceItem);
  87. //当非值类型且类型不相同时
  88. if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType)
  89. {
  90. //判断都是(非泛型、非数组)class
  91. if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass && !sourceItem.PropertyType.IsArray && !targetItem.PropertyType.IsArray && !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
  92. {
  93. var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
  94. memberBindings.Add(Expression.Bind(targetItem, expression));
  95. }
  96. //集合数组类型的转换
  97. if (typeof(IEnumerable).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable).IsAssignableFrom(targetItem.PropertyType))
  98. {
  99. var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
  100. memberBindings.Add(Expression.Bind(targetItem, expression));
  101. }
  102. continue;
  103. }
  104. //可空类型转换到非可空类型,当可空类型值为null时,用默认值赋给目标属性;不为null就直接转换
  105. if (MapperTools.IsNullableType(sourceItem.PropertyType) && !MapperTools.IsNullableType(targetItem.PropertyType))
  106. {
  107. var hasValueExpression = Expression.Equal(Expression.Property(sourceProperty, "HasValue"), Expression.Constant(true));
  108. var conditionItem = Expression.Condition(hasValueExpression, Expression.Convert(sourceProperty, targetItem.PropertyType), Expression.Default(targetItem.PropertyType));
  109. memberBindings.Add(Expression.Bind(targetItem, conditionItem));
  110. continue;
  111. }
  112. //非可空类型转换到可空类型,直接转换
  113. if (!MapperTools.IsNullableType(sourceItem.PropertyType) && MapperTools.IsNullableType(targetItem.PropertyType))
  114. {
  115. var memberExpression = Expression.Convert(sourceProperty, targetItem.PropertyType);
  116. memberBindings.Add(Expression.Bind(targetItem, memberExpression));
  117. continue;
  118. }
  119. if (targetItem.PropertyType != sourceItem.PropertyType)
  120. {
  121. continue;
  122. }
  123. memberBindings.Add(Expression.Bind(targetItem, sourceProperty));
  124. }
  125. //创建一个if条件表达式
  126. var test = Expression.NotEqual(parameter, Expression.Constant(null, sourceType)); // p==null;
  127. var ifTrue = Expression.MemberInit(Expression.New(targetType), memberBindings);
  128. var condition = Expression.Condition(test, ifTrue, Expression.Constant(null, targetType));
  129. var lambda = Expression.Lambda<Func<TSource, TDest>>(condition, parameter);
  130. return lambda.Compile();
  131. }
  132. /// <summary>
  133. /// 类型是clas时赋值
  134. /// </summary>
  135. /// <param name="sourceProperty"></param>
  136. /// <param name="sourceType"></param>
  137. /// <param name="targetType"></param>
  138. /// <returns></returns>
  139. private static Expression GetClassExpression(Expression sourceProperty, Type sourceType, Type targetType)
  140. {
  141. //条件p.Item!=null
  142. var testItem = Expression.NotEqual(sourceProperty, Expression.Constant(null, sourceType));
  143. //构造回调
  144. var mapperType = typeof(ExpressionGenericMapper<,>).MakeGenericType(sourceType, targetType);
  145. var iftrue = Expression.Call(mapperType.GetMethod(nameof(Map), new[]
  146. {
  147. sourceType
  148. }), sourceProperty);
  149. var conditionItem = Expression.Condition(testItem, iftrue, Expression.Constant(null, targetType));
  150. return conditionItem;
  151. }
  152. /// <summary>
  153. /// 类型为集合时赋值
  154. /// </summary>
  155. /// <param name="sourceProperty"></param>
  156. /// <param name="sourceType"></param>
  157. /// <param name="targetType"></param>
  158. /// <returns></returns>
  159. private static Expression GetListExpression(Expression sourceProperty, Type sourceType, Type targetType)
  160. {
  161. //条件p.Item!=null
  162. var testItem = Expression.NotEqual(sourceProperty, Expression.Constant(null, sourceType));
  163. //构造回调
  164. var sourceArg = sourceType.IsArray ? sourceType.GetElementType() : sourceType.GetGenericArguments()[0];
  165. var targetArg = targetType.IsArray ? targetType.GetElementType() : targetType.GetGenericArguments()[0];
  166. var mapperType = typeof(ExpressionGenericMapper<,>).MakeGenericType(sourceArg, targetArg);
  167. var mapperExecMap = Expression.Call(mapperType.GetMethod(nameof(MapList), new[]
  168. {
  169. sourceType
  170. }), sourceProperty);
  171. Expression iftrue;
  172. if (targetType == mapperExecMap.Type)
  173. {
  174. iftrue = mapperExecMap;
  175. }
  176. else if (targetType.IsArray) //数组类型调用ToArray()方法
  177. {
  178. iftrue = Expression.Call(typeof(Enumerable), nameof(Enumerable.ToArray), new[]
  179. {
  180. mapperExecMap.Type.GenericTypeArguments[0]
  181. }, mapperExecMap);
  182. }
  183. else if (typeof(IDictionary).IsAssignableFrom(targetType))
  184. {
  185. iftrue = Expression.Constant(null, targetType); //字典类型不转换
  186. }
  187. else
  188. {
  189. iftrue = Expression.Convert(mapperExecMap, targetType);
  190. }
  191. var conditionItem = Expression.Condition(testItem, iftrue, Expression.Constant(null, targetType));
  192. return conditionItem;
  193. }
  194. private static Action<TSource, TDest> GetMapAction()
  195. {
  196. var sourceType = typeof(TSource);
  197. var targetType = typeof(TDest);
  198. if (MapperTools.IsEnumerable(sourceType) || MapperTools.IsEnumerable(targetType))
  199. {
  200. throw new NotSupportedException("数组类型暂不支持对象映射,请使用List类型");
  201. }
  202. //Func委托传入变量
  203. var sourceParameter = Expression.Parameter(sourceType, "p");
  204. var targetParameter = Expression.Parameter(targetType, "t");
  205. //创建一个表达式集合
  206. var expressions = new List<Expression>();
  207. var targetTypes = targetType.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite);
  208. foreach (var targetItem in targetTypes)
  209. {
  210. var sourceItem = sourceType.GetProperty(targetItem.Name);
  211. //判断对象的读写权限
  212. if (sourceItem == null || !sourceItem.CanRead || sourceItem.PropertyType.IsNotPublic)
  213. {
  214. continue;
  215. }
  216. //标注NotMapped特性的属性忽略转换
  217. if (sourceItem.GetCustomAttribute<NotMappedAttribute>() != null)
  218. {
  219. continue;
  220. }
  221. var sourceProperty = Expression.Property(sourceParameter, sourceItem);
  222. var targetProperty = Expression.Property(targetParameter, targetItem);
  223. //当非值类型且类型不相同时
  224. if (!sourceItem.PropertyType.IsValueType && sourceItem.PropertyType != targetItem.PropertyType)
  225. {
  226. //判断都是(非泛型、非数组)class
  227. if (sourceItem.PropertyType.IsClass && targetItem.PropertyType.IsClass && !sourceItem.PropertyType.IsArray && !targetItem.PropertyType.IsArray && !sourceItem.PropertyType.IsGenericType && !targetItem.PropertyType.IsGenericType)
  228. {
  229. var expression = GetClassExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
  230. expressions.Add(Expression.Assign(targetProperty, expression));
  231. }
  232. //集合数组类型的转换
  233. if (typeof(IEnumerable).IsAssignableFrom(sourceItem.PropertyType) && typeof(IEnumerable).IsAssignableFrom(targetItem.PropertyType))
  234. {
  235. var expression = GetListExpression(sourceProperty, sourceItem.PropertyType, targetItem.PropertyType);
  236. expressions.Add(Expression.Assign(targetProperty, expression));
  237. }
  238. continue;
  239. }
  240. //可空类型转换到非可空类型,当可空类型值为null时,用默认值赋给目标属性;不为null就直接转换
  241. if (MapperTools.IsNullableType(sourceItem.PropertyType) && !MapperTools.IsNullableType(targetItem.PropertyType))
  242. {
  243. var hasValueExpression = Expression.Equal(Expression.Property(sourceProperty, "HasValue"), Expression.Constant(true));
  244. var conditionItem = Expression.Condition(hasValueExpression, Expression.Convert(sourceProperty, targetItem.PropertyType), Expression.Default(targetItem.PropertyType));
  245. expressions.Add(Expression.Assign(targetProperty, conditionItem));
  246. continue;
  247. }
  248. //非可空类型转换到可空类型,直接转换
  249. if (!MapperTools.IsNullableType(sourceItem.PropertyType) && MapperTools.IsNullableType(targetItem.PropertyType))
  250. {
  251. var memberExpression = Expression.Convert(sourceProperty, targetItem.PropertyType);
  252. expressions.Add(Expression.Assign(targetProperty, memberExpression));
  253. continue;
  254. }
  255. if (targetItem.PropertyType != sourceItem.PropertyType)
  256. {
  257. continue;
  258. }
  259. expressions.Add(Expression.Assign(targetProperty, sourceProperty));
  260. }
  261. //当Target!=null判断source是否为空
  262. var testSource = Expression.NotEqual(sourceParameter, Expression.Constant(null, sourceType));
  263. var ifTrueSource = Expression.Block(expressions);
  264. var conditionSource = Expression.IfThen(testSource, ifTrueSource);
  265. //判断target是否为空
  266. var tesTDest = Expression.NotEqual(targetParameter, Expression.Constant(null, targetType));
  267. var conditionTarget = Expression.IfThen(tesTDest, conditionSource);
  268. var lambda = Expression.Lambda<Action<TSource, TDest>>(conditionTarget, sourceParameter, targetParameter);
  269. return lambda.Compile();
  270. }
  271. /// <summary>
  272. /// 新建目标类型实例,并将源对象的属性值拷贝至目标对象的对应属性
  273. /// </summary>
  274. /// <param name="source">源对象实例</param>
  275. /// <returns>深拷贝了源对象属性的目标对象实例</returns>
  276. public static TDest Copy(TSource source)
  277. {
  278. if (source == null) return default(TDest);
  279. // 因为对于泛型类型而言,每次传入不同的泛型参数都会调用静态构造函数,所以可以通过这种方式进行缓存
  280. if (_copyFunc != null)
  281. {
  282. // 如果之前缓存过,则直接调用缓存的委托
  283. return _copyFunc(source);
  284. }
  285. Type sourceType = typeof(TSource);
  286. Type targetType = typeof(TDest);
  287. var paramExpr = Expression.Parameter(sourceType, nameof(source));
  288. Expression bodyExpr;
  289. // 如果对象可以遍历(目前只支持数组和ICollection<T>实现类)
  290. if (sourceType == targetType && MapperTools.IsIEnumerableExceptString(sourceType))
  291. {
  292. bodyExpr = Expression.Call(null, EnumerableCopier.GetMethondInfo(sourceType), paramExpr);
  293. }
  294. else
  295. {
  296. var memberBindings = new List<MemberBinding>();
  297. // 遍历目标对象的所有属性信息
  298. foreach (var targetPropInfo in targetType.GetProperties())
  299. {
  300. // 从源对象获取同名的属性信息
  301. var sourcePropInfo = sourceType.GetProperty(targetPropInfo.Name);
  302. Type sourcePropType = sourcePropInfo?.PropertyType;
  303. Type targetPropType = targetPropInfo.PropertyType;
  304. // 只在满足以下三个条件的情况下进行拷贝
  305. // 1.源属性类型和目标属性类型一致
  306. // 2.源属性可读
  307. // 3.目标属性可写
  308. if (sourcePropType == targetPropType && sourcePropInfo.CanRead && targetPropInfo.CanWrite)
  309. {
  310. // 获取属性值的表达式
  311. Expression expression = Expression.Property(paramExpr, sourcePropInfo);
  312. // 如果目标属性是值类型或者字符串,则直接做赋值处理
  313. // 暂不考虑目标值类型有非字符串的引用类型这种特殊情况
  314. // 非字符串引用类型做递归处理
  315. if (MapperTools.IsRefTypeExceptString(targetPropType))
  316. {
  317. // 进行递归
  318. if (MapperTools.IsRefTypeExceptString(targetPropType))
  319. {
  320. expression = Expression.Call(null, GetCopyMethodInfo(sourcePropType, targetPropType), expression);
  321. }
  322. }
  323. memberBindings.Add(Expression.Bind(targetPropInfo, expression));
  324. }
  325. }
  326. bodyExpr = Expression.MemberInit(Expression.New(targetType), memberBindings);
  327. }
  328. var lambdaExpr = Expression.Lambda<Func<TSource, TDest>>(bodyExpr, paramExpr);
  329. _copyFunc = lambdaExpr.Compile();
  330. return _copyFunc(source);
  331. }
  332. /// <summary>
  333. /// 新建目标类型实例,并将源对象的属性值拷贝至目标对象的对应属性
  334. /// </summary>
  335. /// <param name="source">源对象实例</param>
  336. /// <param name="target">目标对象实例</param>
  337. public static void Copy(TSource source, TDest target)
  338. {
  339. if (source == null) return;
  340. // 因为对于泛型类型而言,每次传入不同的泛型参数都会调用静态构造函数,所以可以通过这种方式进行缓存
  341. // 如果之前缓存过,则直接调用缓存的委托
  342. if (_copyAction != null)
  343. {
  344. _copyAction(source, target);
  345. return;
  346. }
  347. Type sourceType = typeof(TSource);
  348. Type targetType = typeof(TDest);
  349. // 如果双方都可以被遍历
  350. if (MapperTools.IsIEnumerableExceptString(sourceType) && MapperTools.IsIEnumerableExceptString(targetType))
  351. {
  352. // TODO
  353. // 向已存在的数组或者ICollection<T>拷贝的功能暂不支持
  354. }
  355. else
  356. {
  357. var paramSourceExpr = Expression.Parameter(sourceType, nameof(source));
  358. var paramTargetExpr = Expression.Parameter(targetType, nameof(target));
  359. var binaryExpressions = new List<Expression>();
  360. // 遍历目标对象的所有属性信息
  361. foreach (var targetPropInfo in targetType.GetProperties())
  362. {
  363. // 从源对象获取同名的属性信息
  364. var sourcePropInfo = sourceType.GetProperty(targetPropInfo.Name);
  365. Type sourcePropType = sourcePropInfo?.PropertyType;
  366. Type targetPropType = targetPropInfo.PropertyType;
  367. // 只在满足以下三个条件的情况下进行拷贝
  368. // 1.源属性类型和目标属性类型一致
  369. // 2.源属性可读
  370. // 3.目标属性可写
  371. if (sourcePropType == targetPropType && sourcePropInfo.CanRead && targetPropInfo.CanWrite)
  372. {
  373. // 获取属性值的表达式
  374. Expression expression = Expression.Property(paramSourceExpr, sourcePropInfo);
  375. Expression targetPropExpr = Expression.Property(paramTargetExpr, targetPropInfo);
  376. // 如果目标属性是值类型或者字符串,则直接做赋值处理
  377. // 暂不考虑目标值类型有非字符串的引用类型这种特殊情况
  378. if (MapperTools.IsRefTypeExceptString(targetPropType))
  379. {
  380. expression = Expression.Call(null, GetCopyMethodInfo(sourcePropType, targetPropType), expression);
  381. }
  382. binaryExpressions.Add(Expression.Assign(targetPropExpr, expression));
  383. }
  384. }
  385. Expression bodyExpr = Expression.Block(binaryExpressions);
  386. var lambdaExpr = Expression.Lambda<Action<TSource, TDest>>(bodyExpr, paramSourceExpr, paramTargetExpr);
  387. _copyAction = lambdaExpr.Compile();
  388. _copyAction(source, target);
  389. }
  390. }
  391. private static MethodInfo GetCopyMethodInfo(Type source, Type target) => typeof(ExpressionGenericMapper<,>).MakeGenericType(source, target).GetMethod(nameof(Copy), new[]
  392. {
  393. source
  394. });
  395. }
  396. }