using System; using System.Linq; using System.Reflection; using System.Reflection.Emit; using VCommon.Collections; using VCommon.Reflection; // ReSharper disable StaticMemberInGenericType namespace VCommon.VAutoMapper.Internals { //REF: StackOverflow.com/a/3862241/2430943 internal static class MiddleClass { private static readonly string NameSpace = $"{nameof(VCommon)}.{nameof(VCommon.VAutoMapper)}.MiddleClasses.{nameof(MiddleClass)}"; private static readonly ModuleBuilder ModuleBuilder; static MiddleClass() { var name = new AssemblyName(NameSpace); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule"); } public static Type Create(Type entity, Type dto) { var midPropNames = dto.GetPublicInstanceProperties().Select(p => p.Name).ToArray(); var entityProps = entity.GetPublicInstanceProperties().ToDictionary(p => p.Name); var typeName = $"{NameSpace}.{entity.Assembly.FullName}!{entity.FullName}->{dto.Assembly.FullName}!{dto.FullName}"; var tb = ModuleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoLayout, null); tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); foreach (var propName in midPropNames) { if (false == entityProps.TryGetValue(propName, out var field)) throw new VAutoMapperException($"Missing property [{propName}] in [{entity.FullName}] for [{dto.FullName}]"); var propertyType = field.PropertyType; if (false == propertyType.IsPrimitive) { var primitiveCheck = propertyType.In(typeof(string), typeof(DateTime), typeof(Guid), typeof(decimal), typeof(TimeSpan)); var genericCheck = propertyType.IsGenericType && propertyType.GetGenericTypeDefinition().In(typeof(Nullable<>)); if (genericCheck && primitiveCheck) { //TODO: 复杂类型支持(递归?) throw new NotSupportedException($"Only primitive property support. [{field}] in [{field.DeclaringType?.FullName ?? field.DeclaringType?.Name ?? "Unknown class"}]"); } } CreateProperty(tb, field.Name, propertyType); } var middleType = tb.CreateType(); VAutoMapper.ConfigMapping(entity, middleType); VAutoMapper.ConfigMapping(middleType, dto); VAutoMapper.FlushMapper(); return middleType; } private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType) { var fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); var propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); var getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); var getIl = getPropMthdBldr.GetILGenerator(); getIl.Emit(OpCodes.Ldarg_0); getIl.Emit(OpCodes.Ldfld, fieldBuilder); getIl.Emit(OpCodes.Ret); var setPropMthdBldr = tb.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyType }); var setIl = setPropMthdBldr.GetILGenerator(); var modifyProperty = setIl.DefineLabel(); var exitSet = setIl.DefineLabel(); setIl.MarkLabel(modifyProperty); setIl.Emit(OpCodes.Ldarg_0); setIl.Emit(OpCodes.Ldarg_1); setIl.Emit(OpCodes.Stfld, fieldBuilder); setIl.Emit(OpCodes.Nop); setIl.MarkLabel(exitSet); setIl.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getPropMthdBldr); propertyBuilder.SetSetMethod(setPropMthdBldr); } } }