VAutoMapper.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. using AutoMapper.QueryableExtensions;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using VCommon.Reflection;
  8. using VCommon.VAutoMapper.Internals;
  9. namespace VCommon.VAutoMapper
  10. {
  11. public static class VAutoMapper
  12. {
  13. private static VAutoMapperConfiguration Config { get; } = new VAutoMapperConfiguration();
  14. private static readonly HashSet<Type> PolyfillTypes = new HashSet<Type>();
  15. static VAutoMapper()
  16. {
  17. foreach (var type in TypeFinder.AllTypes) ConfigMapping(type);
  18. FlushMapper();
  19. }
  20. internal static void FlushMapper() => Config.FlushMapper();
  21. internal static void ConfigMapping(Type type)
  22. {
  23. foreach (var autoMapFromAttribute in type.GetCustomAttributes<AutoMapFromAttribute>())
  24. {
  25. foreach (var f in autoMapFromAttribute.FromTypes) ConfigMapping(f, type);
  26. }
  27. foreach (var autoMapToAttribute in type.GetCustomAttributes<AutoMapToAttribute>())
  28. {
  29. foreach (var t in autoMapToAttribute.ToTypes) ConfigMapping(type, t);
  30. }
  31. }
  32. internal static void ConfigMapping(Type from, Type to)
  33. {
  34. var cfg = Config.CreateMap(from, to);
  35. var toProps = to.GetPublicInstanceProperties();
  36. var fromProps = from.GetPublicInstanceProperties();
  37. foreach (var toProp in toProps.Where(p => p.IsDefined(typeof(AutoMapperIgnoreAttribute))))
  38. {
  39. var toAttr = toProp.GetCustomAttribute<AutoMapperIgnoreAttribute>();
  40. if (toAttr.Direction == AutoMapperIgnoreDirection.Both || toAttr.Direction == AutoMapperIgnoreDirection.From) cfg.ForMember(toProp.Name, p => p.Ignore());
  41. }
  42. foreach (var fromProp in fromProps.Where(p => p.IsDefined(typeof(AutoMapperIgnoreAttribute))))
  43. {
  44. var fromAttr = fromProp.GetCustomAttribute<AutoMapperIgnoreAttribute>();
  45. if (fromAttr.Direction == AutoMapperIgnoreDirection.Both || fromAttr.Direction == AutoMapperIgnoreDirection.To) cfg.ForMember(fromProp.Name, p => p.Ignore());
  46. }
  47. foreach (var toProp in toProps.Where(p => p.IsDefined(typeof(AutoMapJsonConvertAttribute))))
  48. {
  49. PolyfillTypes.Add(to);
  50. //from.fromProp:string -deson-> to.toProp:obj
  51. var fromProp = from.GetProperty(toProp.Name);
  52. if (null == fromProp) continue;
  53. if (typeof(string) != fromProp.PropertyType) throw new VAutoMapperException($"Invalid json mapping attribute for [{from.FullName}.{fromProp.Name}] it must be string");
  54. // (object p) => JsonSerializer.Deserialize( (([type])p).[fromProp] ), [toProp].PropertyType )
  55. var dejson = JsonConvertHolder.GetDeserializationExpression(fromProp, toProp);
  56. var paramFrom = Expression.Parameter(typeof(object));
  57. var paramFromUnboxing = Expression.Convert(paramFrom, from);
  58. var callDeJson = Expression.Invoke(dejson, Expression.Property(paramFromUnboxing, fromProp.Name), Expression.Constant(toProp.PropertyType));
  59. var expression = Expression.Lambda<Func<object, object>>(callDeJson, paramFrom);
  60. var func = expression.Compile();
  61. cfg.ForMember(fromProp.Name, x => x.MapFrom(obj => func(obj)));
  62. }
  63. foreach (var fromProp in fromProps.Where(p => p.IsDefined(typeof(AutoMapJsonConvertAttribute))))
  64. {
  65. PolyfillTypes.Add(from);
  66. //from.fromProp:obj -json-> to.toProp:string
  67. var toProp = to.GetProperty(fromProp.Name);
  68. if (null == toProp) continue;
  69. if (typeof(string) != toProp.PropertyType) throw new VAutoMapperException($"Invalid mapping attribute on {to.FullName}.{toProp.Name} it must be string");
  70. // (object p) => JsonSerializer.Serialize( (([type])p).[fromProp] ))
  71. var json = JsonConvertHolder.GetSerializationExpression(fromProp, toProp);
  72. var paramFrom = Expression.Parameter(typeof(object));
  73. var paramFromUnboxing = Expression.Convert(paramFrom, from);
  74. var callJson = Expression.Invoke(json, Expression.Property(paramFromUnboxing, fromProp.Name));
  75. var expression = Expression.Lambda<Func<object, object>>(callJson, paramFrom);
  76. var func = expression.Compile();
  77. cfg.ForMember(fromProp.Name, x => x.MapFrom(obj => func(obj)));
  78. }
  79. }
  80. //---------------------------------------
  81. public static TD MapTo<TD>(this object from) => Config.Mapper.Map<TD>(from);
  82. public static TD MapTo<TF, TD>(this TF from, TD to) => Config.Mapper.Map(from, to);
  83. public static TD[] ProjectionToArray<TF, TD>(this IQueryable<TF> queryable)
  84. {
  85. return PolyfillTypes.Contains(typeof(TD))
  86. ? MiddleMapper<TD>.ProjectionToArray(queryable)
  87. : queryable.ProjectTo<TD>(Config.Config).ToArray();
  88. }
  89. public static TD ProjectionFirstOrDefault<TF, TD>(this IQueryable<TF> queryable)
  90. {
  91. return PolyfillTypes.Contains(typeof(TD))
  92. ? MiddleMapper<TD>.ProjectionToArray(queryable).FirstOrDefault()
  93. : queryable.ProjectTo<TD>(Config.Config).FirstOrDefault();
  94. }
  95. }
  96. }