EntityMapper 高性能实体映射工具

 public static class EntityMapper
 {
     #region 配置和缓存

     public class MapperConfig
     {
         public bool IgnoreNullValues { get; set; }
         public Dictionary<string, string> PropertyAliases { get; } = new();
         public static MapperConfig Default => 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, Type), Func<object, object>> _typeConverters = new();
     private static readonly ConcurrentDictionary<Type, Func<object>> _activatorCache = new();

     static EntityMapper()
     {
         // 注册默认类型转换器
         RegisterConverter<int, long>(v => v);
         RegisterConverter<long, int>(v => (int)v);
         RegisterConverter<string, DateTime>(DateTime.Parse);
         RegisterConverter<DateTime, string>(v => v.ToString("yyyy-MM-dd"));
         RegisterConverter<string, Guid>(Guid.Parse);
         RegisterConverter<Guid, string>(v => v.ToString());
     }

     #endregion

     #region 公共API

     /// <summary>注册自定义类型转换器</summary>
     public static void RegisterConverter<TFrom, TTo>(Func<TFrom, TTo> converter)
     {
         _typeConverters[(typeof(TFrom), typeof(TTo))] = v => converter((TFrom)v);

         // 自动支持可空类型转换
         if (typeof(TFrom).IsValueType && !IsNullable(typeof(TFrom)))
         {
             var nullableFromType = typeof(Nullable<>).MakeGenericType(typeof(TFrom));
             _typeConverters[(nullableFromType, typeof(TTo))] = v => v == null ? default : converter((TFrom)v);
         }
     }

     /// <summary>对象映射</summary>
     public static TTarget Map<TSource, TTarget>(
         this TSource source,
         TTarget target = default,
         MapperConfig config = null) where TTarget : new()
     {
         if (source == null) return default;

         config ??= MapperConfig.Default;

         // 循环引用检测
         if (_mappingStack.Value.Contains(source))
             throw new InvalidOperationException($"检测到循环引用: {typeof(TSource)}");

         _mappingStack.Value.Push(source);
         try
         {
             target ??= CreateInstance<TTarget>();
             GetMapper<TSource, TTarget>()(source, target, config);
             return target;
         }
         catch (Exception ex)
         {
             throw new InvalidOperationException($"映射 {typeof(TSource)} → {typeof(TTarget)} 失败: {ex.Message}", ex);
         }
         finally
         {
             _mappingStack.Value.Pop();
         }
     }

     /// <summary>批量映射</summary>
     public static List<TTarget> MapList<TSource, TTarget>(
         this IEnumerable<TSource> sources,
         MapperConfig config = null) where TTarget : new()
     {
         if (sources == null) return null;

         config ??= MapperConfig.Default;
         var mapper = GetMapper<TSource, TTarget>();
         var results = new List<TTarget>();

         foreach (var source in sources)
         {
             var target = CreateInstance<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,
                     targetProp,
                     targetParam,
                     configParam);

                 if (assignExpr != null) expressions.Add(assignExpr);
             }
             // 修复:确保Block至少有一个表达式
             var body = expressions.Count == 0
                 ? Expression.Block(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 config = new MapperConfig();
         if (config.PropertyAliases.TryGetValue(targetPropName, out var sourcePropName))
         {
             return props.FirstOrDefault(p => p.Name == sourcePropName);
         }

         return props.FirstOrDefault(p => p.Name == targetPropName);
     }

     private static Expression BuildPropertyAssignment(
   MemberExpression sourceValue,
   PropertyInfo targetProp,
   ParameterExpression targetParam,
   ParameterExpression configParam)
     {
         var targetPropExpr = Expression.Property(targetParam, targetProp);
         var ignoreNull = Expression.Property(configParam, nameof(MapperConfig.IgnoreNullValues));

         // 优化后的null值表达式
         Expression nullValue = sourceValue.Type.IsValueType && !IsNullable(sourceValue.Type)
             ? Expression.Default(sourceValue.Type)
             : Expression.Constant(null, sourceValue.Type);

         // 类型安全的null检查
         var nullCheck = Expression.Equal(
             sourceValue,
             nullValue);

         // 如果IgnoreNullValues为true且值为null,则跳过赋值
         var condition = Expression.IfThen(
             Expression.Not(Expression.AndAlso(ignoreNull, nullCheck)),
             Expression.Assign(targetPropExpr,
                 BuildValueExpression(sourceValue, targetProp.PropertyType, configParam)));

         return condition;
     }
     private static Expression BuildValueExpression(
 Expression sourceValue,
 Type targetType,
 ParameterExpression configParam)
     {
         var sourceType = sourceValue.Type;

         // 1. 类型相同直接赋值
         if (sourceType == targetType) return sourceValue;

         // 2. 处理可空类型转换
         if (IsNullable(sourceType) || IsNullable(targetType))
         {
             var underlyingSourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
             var underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;

             if (underlyingSourceType == underlyingTargetType)
             {
                 return Expression.Convert(sourceValue, targetType);
             }
         }

         // 3. 使用注册的类型转换器
         if (_typeConverters.TryGetValue((sourceType, targetType), out var converter))
         {
             return Expression.Convert(
                 Expression.Invoke(Expression.Constant(converter), sourceValue),
                 targetType);
         }

         // 4. 默认类型转换
         return Expression.Convert(sourceValue, targetType);
     }

     #endregion

     #region 辅助方法

     private static T CreateInstance<T>() where T : new()
     {
         return _activatorCache.GetOrAdd(typeof(T), t =>
         {
             var newExpr = Expression.New(typeof(T));
             var lambda = Expression.Lambda<Func<object>>(newExpr);
             return lambda.Compile();
         })() is T instance ? instance : new T();
     }

     private static bool IsNullable(Type type)
     {
         return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
     }

     private static bool IsSimpleType(Type type)
     {
         return type.IsPrimitive
             || type.IsEnum
             || type == typeof(string)
             || type == typeof(decimal)
             || type == typeof(DateTime)
             || type == typeof(TimeSpan)
             || type == typeof(Guid);
     }

     #endregion
 }

  

基于表达式树实现的高性能实体映射工具,支持嵌套对象、集合、类型转换和属性别名等功能

主要特性

  1. ​​高性能​​:使用表达式树编译委托,避免了反射的性能开销
  2. ​​线程安全​​:使用 ConcurrentDictionary 和 ThreadLocal 保证线程安全
  3. ​​循环引用检测​​:通过栈结构检测并防止循环引用
  4. ​​类型转换支持​​:
    • 内置常见类型转换(int/long, string/DateTime等)
    • 支持自定义类型转换器注册
    • 自动处理可空类型
  5. ​​灵活配置​​:
    • 可忽略null值
    • 支持属性别名映射
  6. ​​批量映射​​:支持集合类型的批量映射

核心实现

1. 缓存机制

  • _propertyCache:缓存类型的属性信息
  • _mapperCache:缓存编译后的映射委托
  • _typeConverters:缓存类型转换器
  • _activatorCache:缓存对象创建委托

2. 映射流程

  1. 获取或创建映射委托(GetMapper
  2. 为每个可写属性构建赋值表达式(BuildPropertyAssignment
  3. 处理类型转换(BuildValueExpression
  4. 编译表达式树为委托并执行

3. 表达式树构建

  • 动态构建属性访问、类型检查和转换表达式
  • 处理null值检查(考虑配置的IgnoreNullValues
  • 处理类型转换(包括注册的自定义转换器)
  • 使用示例

  • // 注册自定义转换器
    EntityMapper.RegisterConverter<string, int>(int.Parse);
    
    // 简单映射
    var userDto = userEntity.Map<UserDto>();
    
    // 批量映射
    var orderDtos = orders.MapList<Order, OrderDto>();
    
    // 使用配置
    var config = new EntityMapper.MapperConfig {
        IgnoreNullValues = true,
        PropertyAliases = { ["DtoName"] = "EntityName" }
    };
    var result = source.Map<Target>(config: config);
    

      

posted @ 2025-05-28 11:05  煤炭g  阅读(11)  评论(0)    收藏  举报