You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求跨数据源实体映射的优雅泛型实现方案

优雅实现泛型数据映射的解决方案

针对你遇到的泛型方法中调用重载映射方法的问题,这里提供几种轻量且符合开闭原则的解决方案,替代硬编码判断或笨重的设计模式:

方案一:委托字典注册映射(推荐,无侵入+高性能)

通过静态字典注册每个TA到TB的映射委托,避免反射开销,同时保持扩展性:

public static class Mapper
{
    private static readonly Dictionary<Type, Delegate> _typeMappings = new Dictionary<Type, Delegate>();

    // 静态构造函数初始化所有映射关系
    static Mapper()
    {
        Register<ClassA1, ClassB1>(a => new ClassB1());
        Register<ClassA2, ClassB2>(a => new ClassB2());
        // 新增类型对时,在此添加一行Register即可
    }

    // 对外暴露的注册方法(如需动态添加)
    public static void Register<TA, TB>(Func<TA, TB> mappingFunc)
    {
        _typeMappings[typeof(TA)] = mappingFunc;
    }

    // 单个实体映射
    public static TB Map<TA, TB>(TA source)
    {
        if (_typeMappings.TryGetValue(typeof(TA), out var del) && del is Func<TA, TB> mapper)
            return mapper(source);
        
        throw new InvalidOperationException($"未找到{typeof(TA)}到{typeof(TB)}的映射关系");
    }

    // 批量List映射
    public static List<TB> Map<TA, TB>(List<TA> sourceList)
    {
        return sourceList.Select(Map<TA, TB>).ToList();
    }
}

此时你的ProcessEntity方法可以直接正常调用:

public void ProcessEntity<TA, TB>(string EntityNameInSourceA, string EntityNameInSourceB)
{
    List<TA> dataA = Source1.GetData<TA>(EntityNameInSourceA);
    List<TB> dataB = Mapper.Map<TA, TB>(dataA);
    Source2.SaveData<TB>(dataB);
}

新增类型对时,只需在Mapper的静态构造函数中添加一行Register<ClassA4, ClassB4>(a => new ClassB4());,再调用ProcessEntity<ClassA4, ClassB4>("TableA4", "TableB4");即可,完全符合你的需求。

方案二:扩展泛型映射方法(兼容原有重载)

如果你想保留原有的重载Map方法,可通过反射封装泛型批量映射(注意:反射有一定性能损耗,适合性能要求不高的场景):

public static class Mapper
{
    // 保留原有重载方法
    public static ClassB1 Map(ClassA1 source) => new ClassB1();
    public static ClassB2 Map(ClassA2 source) => new ClassB2();
    // ...

    // 缓存映射方法,避免每次反射查找
    private static readonly Dictionary<Type, MethodInfo> _methodCache = new Dictionary<Type, MethodInfo>();

    public static List<TB> Map<TA, TB>(List<TA> sourceList)
    {
        var sourceType = typeof(TA);
        if (!_methodCache.TryGetValue(sourceType, out var mapMethod))
        {
            mapMethod = typeof(Mapper).GetMethod("Map", new[] { sourceType });
            if (mapMethod == null)
                throw new InvalidOperationException($"未找到{sourceType}对应的映射方法");
            
            _methodCache[sourceType] = mapMethod;
        }

        return sourceList.Select(item => (TB)mapMethod.Invoke(null, new[] { item })).ToList();
    }
}

方案三:接口约束(侵入式但最简洁)

如果你有权修改SourceA的实体类,可以让它们实现统一的映射接口,彻底摆脱静态Mapper类:

// 定义映射接口
public interface IMappable<TB>
{
    TB MapTo();
}

// 让SourceA的实体实现接口
public class ClassA1 : IMappable<ClassB1>
{
    public ClassB1 MapTo() => new ClassB1();
}

public class ClassA2 : IMappable<ClassB2>
{
    public ClassB2 MapTo() => new ClassB2();
}

// 泛型批量映射扩展
public static class MappingExtensions
{
    public static List<TB> MapAll<TA, TB>(this List<TA> sourceList) where TA : IMappable<TB>
    {
        return sourceList.Select(a => a.MapTo()).ToList();
    }
}

此时ProcessEntity方法可简化为:

public void ProcessEntity<TA, TB>(string EntityNameInSourceA, string EntityNameInSourceB) where TA : IMappable<TB>
{
    List<TA> dataA = Source1.GetData<TA>(EntityNameInSourceA);
    List<TB> dataB = dataA.MapAll<TA, TB>();
    Source2.SaveData<TB>(dataB);
}

方案对比

  • AutoMapper:成熟第三方库,无需手动编写映射逻辑,但会引入外部依赖,适合复杂映射场景。
  • 硬编码if判断:扩展性极差,新增类型需修改判断逻辑,违反开闭原则,不推荐。
  • 适配器模式:过于笨重,为每个类型对编写适配器类会导致类爆炸,不适用于此场景。

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 04:26:00