MiddleClass.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using System.Reflection.Emit;
  5. using VCommon.Collections;
  6. using VCommon.Reflection;
  7. // ReSharper disable StaticMemberInGenericType
  8. namespace VCommon.VAutoMapper.Internals
  9. {
  10. //REF: StackOverflow.com/a/3862241/2430943
  11. internal static class MiddleClass
  12. {
  13. private static readonly string NameSpace = $"{nameof(VCommon)}.{nameof(VCommon.VAutoMapper)}.MiddleClasses.{nameof(MiddleClass)}";
  14. private static readonly ModuleBuilder ModuleBuilder;
  15. static MiddleClass()
  16. {
  17. var name = new AssemblyName(NameSpace);
  18. var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
  19. ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
  20. }
  21. public static Type Create(Type entity, Type dto)
  22. {
  23. var midPropNames = dto.GetPublicInstanceProperties().Select(p => p.Name).ToArray();
  24. var entityProps = entity.GetPublicInstanceProperties().ToDictionary(p => p.Name);
  25. var typeName = $"{NameSpace}.{entity.Assembly.FullName}!{entity.FullName}->{dto.Assembly.FullName}!{dto.FullName}";
  26. var tb = ModuleBuilder.DefineType(typeName,
  27. TypeAttributes.Public |
  28. TypeAttributes.Class |
  29. TypeAttributes.AutoLayout,
  30. null);
  31. tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
  32. foreach (var propName in midPropNames)
  33. {
  34. if (false == entityProps.TryGetValue(propName, out var field))
  35. throw new VAutoMapperException($"Missing property [{propName}] in [{entity.FullName}] for [{dto.FullName}]");
  36. var propertyType = field.PropertyType;
  37. if (false == propertyType.IsPrimitive)
  38. {
  39. var primitiveCheck = propertyType.In(typeof(string), typeof(DateTime), typeof(Guid), typeof(decimal), typeof(TimeSpan));
  40. var genericCheck = propertyType.IsGenericType && propertyType.GetGenericTypeDefinition().In(typeof(Nullable<>));
  41. if (genericCheck && primitiveCheck)
  42. {
  43. //TODO: 复杂类型支持(递归?)
  44. throw new NotSupportedException($"Only primitive property support. [{field}] in [{field.DeclaringType?.FullName ?? field.DeclaringType?.Name ?? "Unknown class"}]");
  45. }
  46. }
  47. CreateProperty(tb, field.Name, propertyType);
  48. }
  49. var middleType = tb.CreateType();
  50. VAutoMapper.ConfigMapping(entity, middleType);
  51. VAutoMapper.ConfigMapping(middleType, dto);
  52. VAutoMapper.FlushMapper();
  53. return middleType;
  54. }
  55. private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
  56. {
  57. var fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
  58. var propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
  59. var getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
  60. var getIl = getPropMthdBldr.GetILGenerator();
  61. getIl.Emit(OpCodes.Ldarg_0);
  62. getIl.Emit(OpCodes.Ldfld, fieldBuilder);
  63. getIl.Emit(OpCodes.Ret);
  64. var setPropMthdBldr =
  65. tb.DefineMethod("set_" + propertyName,
  66. MethodAttributes.Public |
  67. MethodAttributes.SpecialName |
  68. MethodAttributes.HideBySig,
  69. null, new[] { propertyType });
  70. var setIl = setPropMthdBldr.GetILGenerator();
  71. var modifyProperty = setIl.DefineLabel();
  72. var exitSet = setIl.DefineLabel();
  73. setIl.MarkLabel(modifyProperty);
  74. setIl.Emit(OpCodes.Ldarg_0);
  75. setIl.Emit(OpCodes.Ldarg_1);
  76. setIl.Emit(OpCodes.Stfld, fieldBuilder);
  77. setIl.Emit(OpCodes.Nop);
  78. setIl.MarkLabel(exitSet);
  79. setIl.Emit(OpCodes.Ret);
  80. propertyBuilder.SetGetMethod(getPropMthdBldr);
  81. propertyBuilder.SetSetMethod(setPropMthdBldr);
  82. }
  83. }
  84. }