/// <summary> /// Register a manual mapping information from source to target /// </summary> /// <typeparam name="TSource">Source type</typeparam> /// <typeparam name="TTarget">Target type</typeparam> public void RegisterMap <TSource, TTarget>(Action <TSource, TTarget> convert) { var sourceType = typeof(TSource); var targetType = typeof(TTarget); if (!_mappings.ContainsKey(sourceType)) { _mappings[sourceType] = new Dictionary <Type, ILMappingInfo>(); } ILMappingInfo ilMappingInfo = _mappings[sourceType].ContainsKey(targetType) ? _mappings[sourceType][targetType] : new IlMappingInfo <TSource, TTarget>(); ilMappingInfo.ManualMapper = (s, t) => convert((TSource)s, (TTarget)t); ilMappingInfo.InstanceMapper = (s, t) => convert((TSource)s, (TTarget)t); _mappings[sourceType][targetType] = ilMappingInfo; }
/// <summary> /// Generate dynamic method for collection mapping /// </summary> Func <ILMappingInfo, IList <TSource>, TTarget[]> GenerateCollectionDelegate <TSource, TTarget>(List <PropertyMapper> properties, ILMappingInfo ilMappingInfo) { var sourceType = typeof(TSource); var targetType = typeof(TTarget); var targetCollectionType = typeof(TTarget[]); var targetConstrucotr = targetType.GetConstructor(Type.EmptyTypes); if (targetConstrucotr == null) { throw new ArgumentException("Target type must have a parameterless constructor"); } var targetCollectionConstrucotr = targetCollectionType.GetConstructor(new[] { typeof(int) }); if (targetCollectionConstrucotr == null) { throw new ArgumentException(); } var dm = new DynamicMethod($"_MAP_LIST_{sourceType.Name}_{targetType.Name}", typeof(TTarget[]), new[] { typeof(ILMappingInfo), typeof(IList <TSource>) }, typeof(TTarget).GetTypeInfo().Module); var il = dm.GetILGenerator(); var loopStart = il.DefineLabel(); var loopExit = il.DefineLabel(); // Store argument list in local var mappingInfoLocal = il.DeclareLocal(typeof(ILMappingInfo)); var source = il.DeclareLocal(typeof(IList <TSource>)); var result = il.DeclareLocal(typeof(TTarget[])); var count = il.DeclareLocal(typeof(IList <TSource>)); var index = il.DeclareLocal(typeof(IList <TSource>)); var sourceItem = il.DeclareLocal(typeof(TSource)); var targetItem = il.DeclareLocal(typeof(TTarget)); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Stloc, mappingInfoLocal); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stloc, source); // Define result array ; il.Emit(OpCodes.Ldloc, source); il.Emit(OpCodes.Call, typeof(ICollection).GetMethod("get_Count")); il.Emit(OpCodes.Stloc, count); il.Emit(OpCodes.Ldloc, count); il.Emit(OpCodes.Newarr, typeof(TTarget)); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc, index); // Mark loop start il.MarkLabel(loopStart); //Get item at index il.Emit(OpCodes.Ldloc, source); il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Call, typeof(IList <TSource>).GetMethod("get_Item")); il.Emit(OpCodes.Stloc, sourceItem); // Create new item il.Emit(OpCodes.Newobj, targetConstrucotr); il.Emit(OpCodes.Stloc, targetItem); // Map properties foreach (var property in properties) { EmitPropertyMap(property, il, sourceItem, targetItem); } if (ilMappingInfo.AdditionalMap != null) { var method = ilMappingInfo.GetType().GetMethod(nameof(ILMappingInfo.ExecuteAdditionalMap)); if (method != null) { il.Emit(OpCodes.Ldloc, mappingInfoLocal); il.Emit(OpCodes.Ldloc, sourceItem); il.Emit(OpCodes.Ldloc, targetItem); il.Emit(OpCodes.Call, method); } } // Add item to list il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Ldloc, targetItem); il.Emit(OpCodes.Stelem_Ref); // increment index il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc, index); // Check loop end il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Ldloc, count); il.Emit(OpCodes.Beq, loopExit); // Repeat il.Emit(OpCodes.Br, loopStart); il.MarkLabel(loopExit); il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret); return((Func <ILMappingInfo, IList <TSource>, TTarget[]>)dm.CreateDelegate(typeof(Func <ILMappingInfo, IList <TSource>, TTarget[]>))); }