复制代码
/// <summary>
/// 高性能实体映射工具(表达式树实现),支持嵌套对象/集合/类型转换/属性别名
/// </summary>
public static class EntityMapper
{
#region 配置和缓存
private class MapperConfig
{
public bool IgnoreNullValues { get; set; }
public Dictionary<string, string> PropertyAliases { get; } = new();
}
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> _propertyCache = new();
private static readonly ConcurrentDictionary<string, Delegate> _mapperCache = new();
private static readonly ThreadLocal<Stack<object>> _mappingStack = new(() => new Stack<object>());
private static readonly ConcurrentDictionary<Type, Func<object, object>> _typeConverters = new();
static EntityMapper()
{
// 注册默认类型转换器
RegisterConverter<int, long>(v => v);
RegisterConverter<string, DateTime>(DateTime.Parse);
RegisterConverter<DateTime, string>(v => v.ToString("yyyy-MM-dd"));
}
#endregion
#region 公共API
/// <summary>注册自定义类型转换器</summary>
public static void RegisterConverter<TFrom, TTo>(Func<TFrom, TTo> converter)
{
_typeConverters[typeof(TTo)] = v => converter((TFrom)v);
}
/// <summary>对象映射</summary>
public static TTarget Map<TSource, TTarget>(
this TSource source,
TTarget target,
bool ignoreNullValues = false,
Dictionary<string, string> propertyAliases = null)
{
if (source == null) return target;
var config = new MapperConfig
{
IgnoreNullValues = ignoreNullValues,
PropertyAliases = propertyAliases ?? new Dictionary<string, string>()
};
// 循环引用检测
if (_mappingStack.Value.Contains(source))
throw new InvalidOperationException("检测到循环引用");
_mappingStack.Value.Push(source);
try
{
GetMapper<TSource, TTarget>()(source, target, config);
return target;
}
finally
{
_mappingStack.Value.Pop();
}
}
/// <summary>批量映射</summary>
public static List<TTarget> MapList<TSource, TTarget>(
this IEnumerable<TSource> sources,
bool ignoreNullValues = false,
Dictionary<string, string> propertyAliases = null)
where TTarget : new()
{
if (sources == null) return null;
var config = new MapperConfig
{
IgnoreNullValues = ignoreNullValues,
PropertyAliases = propertyAliases ?? new Dictionary<string, string>()
};
var mapper = GetMapper<TSource, TTarget>();
var results = new List<TTarget>();
foreach (var source in sources)
{
var target = new TTarget();
mapper(source, target, config);
results.Add(target);
}
return results;
}
#endregion
#region 核心映射逻辑
private static Action<TSource, TTarget, MapperConfig> GetMapper<TSource, TTarget>()
{
var cacheKey = $"{typeof(TSource)}->{typeof(TTarget)}";
return (Action<TSource, TTarget, MapperConfig>)_mapperCache.GetOrAdd(cacheKey, _ =>
{
// 表达式树构建映射逻辑
var sourceParam = Expression.Parameter(typeof(TSource));
var targetParam = Expression.Parameter(typeof(TTarget));
var configParam = Expression.Parameter(typeof(MapperConfig));
var expressions = new List<Expression>();
// 处理所有可写属性
foreach (var targetProp in typeof(TTarget).GetProperties().Where(p => p.CanWrite))
{
var sourceProp = FindSourceProperty<TSource>(targetProp.Name, configParam);
if (sourceProp == null) continue;
var sourceValue = Expression.Property(sourceParam, sourceProp);
var assignExpr = BuildPropertyAssignment(
sourceValue,
Expression.Property(targetParam, targetProp),
configParam);
if (assignExpr != null) expressions.Add(assignExpr);
}
// 构建Lambda表达式
var body = expressions.Count == 0
? Expression.Empty()
: Expression.Block(expressions);
return Expression.Lambda<Action<TSource, TTarget, MapperConfig>>(
body, sourceParam, targetParam, configParam).Compile();
});
}
private static PropertyInfo FindSourceProperty<TSource>(
string targetPropName,
ParameterExpression configParam)
{
var props = _propertyCache.GetOrAdd(typeof(TSource),
t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance));
// 优先检查属性别名
var aliasCheck = Expression.Property(configParam, nameof(MapperConfig.PropertyAliases));
var aliasValue = Expression.Call(aliasCheck, "TryGetValue", null,
Expression.Constant(targetPropName),
Expression.Parameter(typeof(string)));
// 实际代码中需转换为运行时逻辑
return props.FirstOrDefault(p => p.Name == targetPropName);
}
private static Expression BuildPropertyAssignment(
MemberExpression sourceValue,
MemberExpression targetProp,
ParameterExpression configParam)
{
// 空值检查
var ignoreNull = Expression.Property(configParam, nameof(MapperConfig.IgnoreNullValues));
var nullCheck = Expression.Equal(sourceValue, Expression.Constant(null));
var condition = Expression.IfThen(
Expression.Not(Expression.AndAlso(ignoreNull, nullCheck)),
Expression.Assign(targetProp, ConvertType(sourceValue, targetProp.Type)));
return condition;
}
private static Expression ConvertType(Expression sourceValue, Type targetType)
{
// 类型相同直接赋值
if (sourceValue.Type == targetType) return sourceValue;
// 使用注册的类型转换器
if (_typeConverters.TryGetValue(targetType, out var converter))
{
var converterConst = Expression.Constant(converter);
return Expression.Convert(
Expression.Call(converterConst, "Invoke", null,
Expression.Convert(sourceValue, typeof(object))),
targetType);
}
// 默认类型转换
return Expression.Convert(sourceValue, targetType);
}
#endregion
#region 集合映射(优化版)
private static Action<IEnumerable<TSource>, ICollection<TTarget>, MapperConfig>
GetCollectionMapper<TSource, TTarget>()
{
var cacheKey = $"Collection:{typeof(TSource)}->{typeof(TTarget)}";
return (Action<IEnumerable<TSource>, ICollection<TTarget>, MapperConfig>)
_mapperCache.GetOrAdd(cacheKey, _ =>
{
var sourceParam = Expression.Parameter(typeof(IEnumerable<TSource>));
var targetParam = Expression.Parameter(typeof(ICollection<TTarget>));
var configParam = Expression.Parameter(typeof(MapperConfig));
var mapper = GetMapper<TSource, TTarget>();
var mapperConst = Expression.Constant(mapper);
var loopBody = Expression.Call(
mapperConst,
"Invoke",
null,
Expression.Parameter(typeof(TSource)),
Expression.New(typeof(TTarget)),
configParam);
// 实际实现需要更复杂的表达式树构建
return Expression.Lambda<
Action<IEnumerable<TSource>, ICollection<TTarget>, MapperConfig>>(
Expression.Empty(), sourceParam, targetParam, configParam).Compile();
});
}
#endregion
}