diff --git a/src/AutoGenMapperGenerator/AutoGenMapperGenerator.csproj b/src/AutoGenMapperGenerator/AutoGenMapperGenerator.csproj
index f8a1212..fd930b1 100644
--- a/src/AutoGenMapperGenerator/AutoGenMapperGenerator.csproj
+++ b/src/AutoGenMapperGenerator/AutoGenMapperGenerator.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;net6.0;net8.0;net10.0
+ net6.0;net8.0;net10.0
latest
enable
true
@@ -25,4 +25,8 @@
+
+
+
+
diff --git a/src/AutoGenMapperGenerator/GMapper.cs b/src/AutoGenMapperGenerator/GMapper.cs
index 6a19609..b70cf6f 100644
--- a/src/AutoGenMapperGenerator/GMapper.cs
+++ b/src/AutoGenMapperGenerator/GMapper.cs
@@ -1,4 +1,10 @@
-namespace AutoGenMapperGenerator;
+using AutoGenMapperGenerator.ReflectMapper;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace AutoGenMapperGenerator;
///
/// G -> Generator
@@ -13,21 +19,59 @@ public static class GMapper
///
///
///
- public static TTarget Map(TSource source)
+ public static TTarget Map<
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
+#endif
+ TSource,
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
+#endif
+ TTarget>(this TSource source)
{
+ if (source is null)
+ {
+ return default!;
+ }
if (source is IAutoMap m)
{
return m.MapTo();
}
+ var (IsComplex, IsDictionary, IsEnumerable) = ExpressionHelper.IsComplexType(typeof(TSource));
+ if (IsComplex && IsDictionary)
+ {
+ throw new InvalidOperationException("请使用TEntity ToEntity(this IDictionary dict)");
+ }
+ if (IsComplex && IsEnumerable)
+ {
+ throw new InvalidOperationException("不支持集合类型的映射,请使用LINQ的Select方法进行映射");
+ }
return ExpressionMapper.Map(source);
}
-}
-internal static class ExpressionMapper
-{
- public static TTarget Map(TSource source)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IDictionary ToDictionary(this TEntity entity)
+ {
+ if (entity is null)
+ {
+ return new Dictionary();
+ }
+ return ExpressionMapper.MapToDictionary(entity);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static TEntity ToEntity(this IDictionary dict)
{
- // TODO
- throw new System.NotImplementedException();
+ return ExpressionMapper.MapFromDictionary(dict);
}
}
diff --git a/src/AutoGenMapperGenerator/ReflectMapper/DictionaryExtensions.cs b/src/AutoGenMapperGenerator/ReflectMapper/DictionaryExtensions.cs
new file mode 100644
index 0000000..aae838b
--- /dev/null
+++ b/src/AutoGenMapperGenerator/ReflectMapper/DictionaryExtensions.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+
+namespace AutoGenMapperGenerator.ReflectMapper;
+
+internal static class DictionaryExtensions
+{
+ private static readonly Dictionary conversionCache = [];
+
+ private static T TryParse(string str, CultureInfo culture)
+ {
+ var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
+ if (conversionCache.TryGetValue(targetType, out var del))
+ {
+ var parser = (Func)del;
+ return parser(str, culture);
+ }
+ Func parserFunc = targetType switch
+ {
+ Type t when t == typeof(int) => (s, c) => (T)(object)int.Parse(s, NumberStyles.Any, c),
+ Type t when t == typeof(long) => (s, c) => (T)(object)long.Parse(s, NumberStyles.Any, c),
+ Type t when t == typeof(float) => (s, c) => (T)(object)float.Parse(s, NumberStyles.Any, c),
+ Type t when t == typeof(double) => (s, c) => (T)(object)double.Parse(s, NumberStyles.Any, c),
+ Type t when t == typeof(decimal) => (s, c) => (T)(object)decimal.Parse(s, NumberStyles.Any, c),
+ Type t when t == typeof(DateTime) => (s, c) => (T)(object)DateTime.Parse(s, c),
+ Type t when t == typeof(bool) => (s, c) => (T)(object)bool.Parse(s),
+ _ => throw new NotSupportedException($"Type {targetType.FullName} is not supported for parsing.")
+ };
+ conversionCache[targetType] = parserFunc;
+ return parserFunc(str, culture);
+ }
+
+
+ public static bool TryGetValue(this IDictionary dict, string key, out object value)
+ {
+ value = default!;
+ if (!dict.TryGetValue(key, out var dictValue))
+ {
+ return default!;
+ }
+ if (dictValue is null)
+ {
+ return false;
+ }
+ if (typeof(TValue) == typeof(object))
+ {
+ value = (TValue)dictValue;
+ return true;
+ }
+ if (typeof(TValue) == typeof(string))
+ {
+ value = (TValue)(object)dictValue.ToString()!;
+ return true;
+ }
+ if (dictValue is TValue tValue)
+ {
+ value = tValue;
+ return true;
+ }
+ // 处理可空类型
+ var underlyingType = Nullable.GetUnderlyingType(typeof(TValue));
+ var conversionTargetType = underlyingType ?? typeof(TValue);
+ if (conversionTargetType.IsEnum)
+ {
+ value = (TValue)Enum.Parse(conversionTargetType, dictValue.ToString() ?? string.Empty, true);
+ return true;
+ }
+ if (dictValue is string str)
+ {
+ // string转基础类型
+ try
+ {
+ value = TryParse(str, CultureInfo.CurrentCulture);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ // 使用 Convert.ChangeType 兜底
+ try
+ {
+ var convertedValue = Convert.ChangeType(dictValue, conversionTargetType, CultureInfo.CurrentCulture);
+ value = (TValue)convertedValue!;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/AutoGenMapperGenerator/ReflectMapper/ExpressionHelper.cs b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionHelper.cs
new file mode 100644
index 0000000..3edf320
--- /dev/null
+++ b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionHelper.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace AutoGenMapperGenerator.ReflectMapper;
+
+internal static class ExpressionHelper
+{
+ public static (bool IsComplex, bool IsDictionary, bool IsEnumerable) IsComplexType(Type type)
+ {
+ if (type.IsPrimitive ||
+ type == typeof(string) ||
+ type == typeof(decimal) ||
+ type == typeof(DateTime) ||
+ type == typeof(Guid) ||
+ type == typeof(TimeSpan))
+ {
+ return (false, false, false);
+ }
+
+ if (typeof(IDictionary).IsAssignableFrom(type))
+ {
+ return (true, true, false);
+ }
+ var isEnumerable = IsCollectionType(type);
+ return (type.IsClass && type != typeof(string), false, isEnumerable);
+
+ static bool IsCollectionType(Type type)
+ {
+ // 数组
+ if (type.IsArray)
+ {
+ return true;
+ }
+
+ // 泛型接口 IEnumerable
+ if (type.IsGenericType && typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments()) == type)
+ {
+ return true;
+ }
+
+ // 具体集合类 (List, ICollection 等)
+ foreach (var iface in type.GetInterfaces())
+ {
+ if (iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+ public static MemberInfo? GetMemberInfoFromLambda(LambdaExpression lambda)
+ {
+ // 去除 Convert 节点 (例如 Expression> 会产生 Convert(node))
+ var body = lambda.Body is UnaryExpression unary && unary.NodeType == ExpressionType.Convert
+ ? unary.Operand
+ : lambda.Body;
+
+ if (body is MemberExpression memberExpression)
+ {
+ return memberExpression.Member;
+ }
+ return null;
+ }
+
+ public static (Type, string) GetMemberTypeAndNameFromLambda(LambdaExpression lambda)
+ {
+ // 去除 Convert 节点 (例如 Expression> 会产生 Convert(node))
+ var body = lambda.Body is UnaryExpression unary && unary.NodeType == ExpressionType.Convert
+ ? unary.Operand
+ : lambda.Body;
+ if (body is MemberExpression memberExpression)
+ {
+ return (memberExpression.Type, memberExpression.Member.Name);
+ }
+ throw new InvalidOperationException("Lambda expression does not refer to a member.");
+ }
+
+
+ private static readonly MethodInfo CustomStringParseToBoolean = typeof(ExpressionMapper<,>).GetMethod(nameof(CustomStringToBoolean), BindingFlags.Public | BindingFlags.Static)!;
+ private static readonly MethodInfo enumParseMethod = typeof(Enum).GetMethod("Parse", [typeof(Type), typeof(string), typeof(bool)])!;
+ public static string CustomStringToBoolean(string valueString)
+ {
+ return ",是,1,Y,YES,TRUE,".Contains(valueString.ToUpper()) ? "True" : "False";
+ }
+ public static Expression GetConversionExpression(Type SourceType, Expression SourceExpression, Type TargetType, CultureInfo Culture)
+ {
+ Expression TargetExpression;
+ if (TargetType == SourceType)
+ {
+ TargetExpression = SourceExpression;
+ }
+ else if (SourceType == typeof(string))
+ {
+ TargetExpression = GetParseExpression(SourceExpression, TargetType, Culture);
+ }
+ else if (TargetType == typeof(string))
+ {
+ TargetExpression = Expression.Call(SourceExpression, SourceType.GetMethod("ToString", Type.EmptyTypes)!);
+ }
+ else if (TargetType == typeof(bool))
+ {
+ MethodInfo ToBooleanMethod = typeof(Convert).GetMethod("ToBoolean", [SourceType])!;
+ TargetExpression = Expression.Call(ToBooleanMethod, SourceExpression);
+ }
+ else if (SourceType == typeof(byte[]))
+ {
+ TargetExpression = GetArrayHandlerExpression(SourceExpression, TargetType);
+ }
+ else
+ {
+ TargetExpression = ConvertTypeExpression(SourceExpression, SourceType, TargetType);
+ //TargetExpression = Expression.Convert(SourceExpression, TargetType);
+ }
+ return TargetExpression;
+ }
+
+ private static Expression GetArrayHandlerExpression(Expression sourceExpression, Type targetType)
+ {
+ Expression TargetExpression;
+ if (targetType == typeof(byte[]))
+ {
+ TargetExpression = sourceExpression;
+ }
+ else if (targetType == typeof(MemoryStream))
+ {
+ ConstructorInfo ConstructorInfo = targetType.GetConstructor([typeof(byte[])])!;
+ TargetExpression = Expression.New(ConstructorInfo, sourceExpression);
+ }
+ else
+ {
+ throw new Exception("Cannot convert a byte array to " + targetType.Name);
+ }
+ return TargetExpression;
+ }
+ private static Expression GetParseExpression(Expression SourceExpression, Type TargetType, CultureInfo Culture)
+ {
+ Type UnderlyingType = GetUnderlyingType(TargetType);
+ if (UnderlyingType.IsEnum)
+ {
+ MethodCallExpression ParsedEnumExpression = GetEnumParseExpression(SourceExpression, UnderlyingType);
+ //Enum.Parse returns an object that needs to be unboxed
+ return Expression.Convert(ParsedEnumExpression, TargetType);
+ }
+ else
+ {
+ Expression ParseExpression;
+ switch (UnderlyingType.FullName)
+ {
+ case "System.Byte":
+ case "System.UInt16":
+ case "System.UInt32":
+ case "System.UInt64":
+ case "System.SByte":
+ case "System.Int16":
+ case "System.Int32":
+ case "System.Int64":
+ case "System.Double":
+ case "System.Decimal":
+ ParseExpression = GetNumberParseExpression(SourceExpression, UnderlyingType, Culture);
+ break;
+ case "System.DateTime":
+ ParseExpression = GetDateTimeParseExpression(SourceExpression, UnderlyingType, Culture);
+ break;
+ case "System.Boolean":
+ ParseExpression = TryParseStringToBoolean(SourceExpression, UnderlyingType);
+ break;
+ case "System.Char":
+ ParseExpression = GetGenericParseExpression(SourceExpression, UnderlyingType);
+ break;
+ default:
+ throw new Exception(string.Format("Conversion from {0} to {1} is not supported", "String", TargetType));
+ }
+ if (Nullable.GetUnderlyingType(TargetType) == null)
+ {
+ return ParseExpression;
+ }
+ else
+ {
+ //Convert to nullable if necessary
+ return Expression.Convert(ParseExpression, TargetType);
+ }
+ }
+ Expression GetGenericParseExpression(Expression sourceExpression, Type type)
+ {
+ MethodInfo ParseMetod = type.GetMethod("Parse", [typeof(string)])!;
+ MethodCallExpression CallExpression = Expression.Call(ParseMetod, [sourceExpression]);
+ return CallExpression;
+ }
+ Expression GetDateTimeParseExpression(Expression sourceExpression, Type type, CultureInfo culture)
+ {
+ MethodInfo ParseMetod = type.GetMethod("Parse", [typeof(string), typeof(DateTimeFormatInfo)])!;
+ ConstantExpression ProviderExpression = Expression.Constant(culture.DateTimeFormat, typeof(DateTimeFormatInfo));
+ MethodCallExpression CallExpression = Expression.Call(ParseMetod, [sourceExpression, ProviderExpression]);
+ return CallExpression;
+ }
+
+ MethodCallExpression GetEnumParseExpression(Expression sourceExpression, Type type)
+ {
+ //Get the MethodInfo for parsing an Enum
+ //MethodInfo EnumParseMethod = typeof(Enum).GetMethod("Parse", [typeof(Type), typeof(string), typeof(bool)])!;
+ ConstantExpression TargetMemberTypeExpression = Expression.Constant(type);
+ ConstantExpression IgnoreCase = Expression.Constant(true, typeof(bool));
+ //Create an expression the calls the Parse method
+ MethodCallExpression CallExpression = Expression.Call(enumParseMethod, [TargetMemberTypeExpression, sourceExpression, IgnoreCase]);
+ return CallExpression;
+ }
+
+ MethodCallExpression GetNumberParseExpression(Expression sourceExpression, Type type, CultureInfo culture)
+ {
+ MethodInfo ParseMetod = type.GetMethod("Parse", [typeof(string), typeof(NumberFormatInfo)])!;
+ ConstantExpression ProviderExpression = Expression.Constant(culture.NumberFormat, typeof(NumberFormatInfo));
+ MethodCallExpression CallExpression = Expression.Call(ParseMetod, [sourceExpression, ProviderExpression]);
+ return CallExpression;
+ }
+ Expression TryParseStringToBoolean(Expression sourceExpression, Type type)
+ {
+ var valueExpression = Expression.Call(CustomStringParseToBoolean, [sourceExpression]);
+ return GetGenericParseExpression(valueExpression, type);
+ }
+ }
+ private static Type GetUnderlyingType(Type targetType)
+ {
+ return Nullable.GetUnderlyingType(targetType) ?? targetType;
+ }
+
+ static MethodInfo changeType = typeof(Convert).GetMethod("ChangeType", [typeof(object), typeof(Type)])!;
+ static MethodInfo isNullOrEmpty = typeof(string).GetMethod(nameof(string.IsNullOrEmpty))!;
+ private static ConditionalExpression ConvertTypeExpression(Expression source, Type sourceType, Type targetType)
+ {
+ var underType = Nullable.GetUnderlyingType(targetType) ?? targetType;
+ var isNull = Expression.Equal(source, Expression.Constant(null));
+ var stringValue = Expression.Call(source, sourceType.GetMethod("ToString", Type.EmptyTypes)!);
+ var isNullOrEmptyExpression = Expression.Call(isNullOrEmpty, stringValue);
+ var canConvert = Expression.AndAlso(Expression.IsFalse(isNull), Expression.IsFalse(isNullOrEmptyExpression));
+ var finalValue = Expression.Convert(Expression.Call(changeType, source, Expression.Constant(underType)), targetType);
+ return Expression.Condition(canConvert, finalValue, Expression.Default(targetType));
+
+ }
+}
diff --git a/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.Convert.cs b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.Convert.cs
new file mode 100644
index 0000000..271803a
--- /dev/null
+++ b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.Convert.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoGenMapperGenerator.ReflectMapper;
+
+internal static partial class ExpressionMapper
+{
+
+}
diff --git a/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.Dictionary.cs b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.Dictionary.cs
new file mode 100644
index 0000000..0600139
--- /dev/null
+++ b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.Dictionary.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AutoGenMapperGenerator.ReflectMapper;
+internal static class ExpressionMapper
+{
+ private static readonly Func, TEntity> mapFromDictionaryFunc;
+ private static readonly Func> mapToDictionaryFunc;
+ public static TEntity MapFromDictionary(IDictionary dict) => mapFromDictionaryFunc(dict);
+ public static IDictionary MapToDictionary(TEntity entity) => mapToDictionaryFunc(entity);
+ static ExpressionMapper()
+ {
+ mapFromDictionaryFunc = CreateMapFromDelegate();
+ mapToDictionaryFunc = CreateMapToDelegate();
+ }
+
+ private static Func> CreateMapToDelegate()
+ {
+ // 参数: TEntity entity
+ var entityParam = Expression.Parameter(typeof(TEntity), "entity");
+
+ // 1. 创建新字典: new Dictionary()
+ var dictionaryType = typeof(Dictionary);
+ var newDict = Expression.New(dictionaryType);
+
+ // 2. 获取 TEntity 的所有属性
+ var properties = typeof(TEntity)
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanRead);
+
+ // 3. 构建初始化器列表
+ var initializers = new List();
+
+ foreach (var prop in properties)
+ {
+ // 字典的 Add 方法
+ var addMethod = dictionaryType.GetMethod("Add")!;
+
+ // Key: 属性名
+ var keyExpr = Expression.Constant(prop.Name);
+
+ // Value: entity.Property (需要转换为 object)
+ var valueExpr = Expression.MakeMemberAccess(entityParam, prop);
+ var convertedValue = Expression.Convert(valueExpr, typeof(object));
+
+ // 构建 Add 调用: .Add("PropertyName", (object)entity.Property)
+ var elementInit = Expression.ElementInit(addMethod, keyExpr, convertedValue);
+ initializers.Add(elementInit);
+ }
+
+ // 4. 构建集合初始化表达式
+ var initExpr = Expression.ListInit(newDict, initializers);
+
+ // 5. 构建 Lambda
+ var lambda = Expression.Lambda>>(initExpr, entityParam);
+
+ return lambda.Compile();
+ }
+
+ private static readonly MethodInfo tryGetValueMethod = typeof(DictionaryExtensions).GetMethod("TryGetValue")!;
+ private static Func, TEntity> CreateMapFromDelegate()
+ {
+ var dictParam = Expression.Parameter(typeof(IDictionary), "dict");
+
+ // 创建新实体
+ var newExpr = Expression.New(typeof(TEntity));
+ var properties = typeof(TEntity)
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanWrite);
+ var bindings = new List();
+ // 1. 声明一个局部变量用于接收值: out object resultValue
+ ParameterExpression? outVariable = Expression.Variable(typeof(object), "value");
+ var assign = Expression.Assign(outVariable, Expression.Constant(null, typeof(object)));
+ foreach (var prop in properties)
+ {
+ var propType = prop.PropertyType;
+ //var (IsComplex, IsDictionary, IsEnumerable) = ExpressionHelper.IsComplexType(propType);
+
+ var keyConst = Expression.Constant(prop.Name);
+ // 2. 调用 TryGetValue 方法
+ var genericTryGetValueMethod = tryGetValueMethod.MakeGenericMethod(prop.PropertyType);
+ var tryGetValueCall = Expression.Call(genericTryGetValueMethod, dictParam, keyConst, outVariable);
+ //var tryGetValueCall = Expression.Call(dictParam, tryGetValueMethod, keyConst, outVariable);
+
+ // 3. 构建转换逻辑:如果 TryGetValue 返回 true,则转换 outVariable;否则使用默认值
+ //var conversionExpr = ExpressionHelper.GetConversionExpression(typeof(object), outVariable, propType, CultureInfo.CurrentCulture);
+ var conversionExpr = BuildConversionFromDictionaryValue(outVariable, propType);
+
+ // 4. 组合条件表达式: dict.TryGetValue(key, out value) ? Convert(value) : default(TProp)
+ var conditionExpr = Expression.Condition(
+ tryGetValueCall, // 条件: TryGetValue 的结果
+ conversionExpr, // 成功: 转换值
+ Expression.Default(propType) // 失败: 默认值
+ );
+
+ //var block = Expression.Block([outVariable], assign, conditionExpr);
+ var bind = Expression.Bind(prop, conditionExpr);
+ bindings.Add(bind);
+ }
+
+ var memberInit = Expression.MemberInit(newExpr, bindings);
+ var block = Expression.Block([outVariable], assign, memberInit);
+ var lambda = Expression.Lambda, TEntity>>(block, dictParam);
+ return lambda.Compile();
+
+ static Expression BuildConversionFromDictionaryValue(Expression sourceExpr, Type targetType)
+ {
+ // 如果目标类型是 object 或 dynamic,直接返回
+ if (targetType == typeof(object))
+ {
+ return sourceExpr;
+ }
+
+ // 处理可空类型
+ var underlyingType = Nullable.GetUnderlyingType(targetType);
+ var conversionTargetType = underlyingType ?? targetType;
+
+ // 如果是枚举
+ if (conversionTargetType.IsEnum)
+ {
+ // 调用 Enum.Parse
+ var parseMethod = typeof(Enum).GetMethod("Parse", new[] { typeof(Type), typeof(string), typeof(bool) })!;
+ var toStringCall = Expression.Call(sourceExpr, typeof(object).GetMethod("ToString")!);
+ var callExpr = Expression.Call(
+ parseMethod,
+ Expression.Constant(conversionTargetType),
+ toStringCall,
+ Expression.Constant(true)
+ );
+ return Expression.Convert(callExpr, targetType);
+ }
+ return Expression.Convert(sourceExpr, targetType);
+ }
+ }
+}
diff --git a/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.cs b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.cs
new file mode 100644
index 0000000..e654c5b
--- /dev/null
+++ b/src/AutoGenMapperGenerator/ReflectMapper/ExpressionMapper.cs
@@ -0,0 +1,309 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using static AutoGenMapperGenerator.ReflectMapper.ExpressionHelper;
+namespace AutoGenMapperGenerator.ReflectMapper;
+
+internal static partial class ExpressionMapper<
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
+#endif
+TSource,
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
+#endif
+TTarget>
+{
+ private static readonly Func mapFunc;
+
+ public static TTarget Map(TSource source) => mapFunc(source);
+ static ExpressionMapper()
+ {
+ var sourceParam = Expression.Parameter(typeof(TSource), "source");
+
+ // 获取目标类型的属性信息
+ var targetPropsDict = typeof(TTarget)
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanWrite)
+ .ToDictionary(p => p.Name);
+
+ // 获取源类型的属性信息 (用于自动映射)
+ var sourcePropsDict = typeof(TSource)
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanRead)
+ .ToDictionary(p => p.Name);
+
+ NewExpression? targetNew;
+ var bindings = new List();
+ // --- 阶段 1: 处理 Profile (如果存在) ---
+ if (MapperOptions.Instance.TryGetProfile(out var profile))
+ {
+ // 获取 Profile 中定义的所有配置
+ var configurations = profile.GetConfigurations();
+ var ctorParams = profile.GetConstructorParameters();
+ var ignores = profile.GetIgnoreMembers();
+ foreach (var item in ignores)
+ {
+ sourcePropsDict.Remove(item);
+ }
+ if (ctorParams.Count > 0)
+ {
+ targetNew = CreateNewWithParameters(sourceParam, ctorParams, name =>
+ {
+ // 优化: 从自动映射字典中移除已处理的属性,防止后面重复处理
+ sourcePropsDict.Remove(name);
+ });
+ }
+ else
+ {
+ targetNew = Expression.New(typeof(TTarget));
+ }
+ foreach (var config in configurations)
+ {
+ if (config.SourceExpression == null || config.DestinationExpression == null) continue;
+
+ // 1. 解析目标表达式 (例如: t => t.TargetName)
+ var targetMember = GetMemberInfoFromLambda(config.DestinationExpression);
+ if (targetMember == null) continue;
+
+ // 2. 转换源表达式:将 Profile 中定义的表达式转换为基于当前 sourceParam 的表达式
+ // 例如:将 (s) => s.A + s.B 转换为基于当前上下文的表达式
+ var sourceExpressionBody = config.SourceExpression.Body;
+
+ // 关键步骤:替换表达式中的参数
+ var modifiedSourceBody = ReplaceParameter(config.SourceExpression.Parameters[0], sourceParam, sourceExpressionBody);
+
+ // 4. 创建绑定
+ var bind = Expression.Bind(targetMember, modifiedSourceBody);
+ bindings.Add(bind);
+
+ // 优化: 从自动映射字典中移除已处理的属性,防止后面重复处理
+ targetPropsDict.Remove(targetMember.Name);
+ }
+ }
+ else
+ {
+ targetNew = Expression.New(typeof(TTarget));
+ }
+
+ // 剩下的属性按默认规则处理 (名称匹配)
+ foreach (var targetProp in targetPropsDict.Values)
+ {
+ // 检查源对象中是否存在同名属性
+ if (!sourcePropsDict.TryGetValue(targetProp.Name, out var sourceProp))
+ {
+ continue;
+ }
+ var sourceType = sourceProp.PropertyType;
+ var targetType = targetProp.PropertyType;
+ var sp = IsComplexType(sourceType);
+ var tp = IsComplexType(targetType);
+ Expression convertedSource;
+ if ((sp.IsEnumerable || tp.IsEnumerable) && !(sp.IsDictionary || tp.IsDictionary)) // 排除字典
+ {
+ convertedSource = HandleCollectionConversion(
+ Expression.MakeMemberAccess(sourceParam, sourceProp),
+ sourceType,
+ targetType);
+ }
+ else if (sp.IsComplex || tp.IsComplex)
+ {
+ var sourceAccess = Expression.MakeMemberAccess(sourceParam, sourceProp);
+ if (sp.IsDictionary && tp.IsDictionary)
+ {
+ throw new InvalidOperationException("目标类型和数据源类型都是字典");
+ }
+ if (sp.IsDictionary)
+ {
+ var method = typeof(ExpressionMapper<>).MakeGenericType(targetType).GetMethod("MapFromDictionary", [typeof(IDictionary)])!;
+ convertedSource = Expression.Call(method, sourceAccess);
+ }
+ else if (tp.IsDictionary)
+ {
+ var method = typeof(ExpressionMapper<>).MakeGenericType(sourceType).GetMethod("MapToDictionary", [sourceType])!;
+ convertedSource = Expression.Call(method, sourceAccess);
+ }
+ else
+ {
+ var method = typeof(ExpressionMapper<,>).MakeGenericType(sourceType, targetType).GetMethod("Map", [sourceType])!;
+ convertedSource = Expression.Call(method, sourceAccess);
+ }
+ convertedSource = Expression.Condition(Expression.Equal(sourceAccess, Expression.Default(sourceProp.PropertyType)),
+ Expression.Default(targetProp.PropertyType),
+ convertedSource);
+ }
+ else
+ {
+ var sourceAccess = Expression.MakeMemberAccess(sourceParam, sourceProp);
+ convertedSource = GetConversionExpression(
+ sourceProp.PropertyType,
+ sourceAccess,
+ targetProp.PropertyType, System.Globalization.CultureInfo.CurrentCulture);
+ }
+
+ var bind = Expression.Bind(targetProp, convertedSource);
+ bindings.Add(bind);
+ }
+
+ // --- 构建表达式树 ---
+ var memberInit = Expression.MemberInit(targetNew, bindings);
+ var lambda = Expression.Lambda>(memberInit, sourceParam);
+ mapFunc = lambda.Compile();
+ }
+
+ private static NewExpression CreateNewWithParameters(ParameterExpression source
+ , IReadOnlyList<(Type, string)> parameters, Action each)
+ {
+ var ctorInfo = typeof(TTarget).GetConstructors().FirstOrDefault(FindConstructor) ?? throw new InvalidOperationException("No matching constructor found for the specified parameters.");
+
+#pragma warning disable IL2026
+ List cps = [];
+ foreach (var item in parameters)
+ {
+ cps.Add(Expression.Property(source, item.Item2));
+ each.Invoke(item.Item2);
+ }
+ return Expression.New(ctorInfo, cps);
+#pragma warning restore IL2026
+
+ bool FindConstructor(ConstructorInfo ctor)
+ {
+ var pp = ctor.GetParameters();
+ if (pp.Length != parameters.Count) return false;
+ for (int i = 0; i < pp.Length; i++)
+ {
+ if (pp[i].ParameterType != parameters[i].Item1) return false;
+ }
+ return true;
+ }
+ }
+
+ private static Expression HandleCollectionConversion(Expression sourceExpression, Type sourceType, Type targetType)
+ {
+ // 获取元素类型
+ // 例如:List -> elementTye = Source
+ var sourceElementType = GetEnumerableElementType(sourceType);
+ var targetElementType = GetEnumerableElementType(targetType);
+
+ if (sourceElementType == null || targetElementType == null)
+ {
+ // 如果无法获取元素类型,尝试直接转换(可能是 object[] 转 List