需要一个良好的模式来在两个数据源之间映射实体

问题描述 投票:0回答:1

我遇到了一个非常简单的问题,我找不到简单的解决方案。我正在寻找一种优雅的模式来在两个数据源之间映射数据

来源A 来源B

A1 类 -> B1 类

A2 类 -> B2 类

A3 类 -> B3 类

我有一些带有重载方法的静态类,用于在类之间映射数据:

public static class Mapper{

    public static ClassB1 Map(ClassA1){
        return new ClassB1();
    }
  
    public static ClassB2 Map(ClassA2){
        return new ClassB2();
    }

    // etc.
}

我想要一些简单的模式以通用风格工作,比如


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

并使用它

public void Main(){
    ProcessEntity<ClassA1, ClassB1>("TableA1", "TableB1");
    ProcessEntity<ClassA2, ClassB2>("TableA2", "TableB2");
    ProcessEntity<ClassA3, ClassB3>("TableA3", "TableB3");
}

这样我就可以通过修改 Mapper 类并添加新行来简单地添加一对新的类

ProcessEntity<ClassA4, ClassB4>("TableA4", "TableB4");

问题是我不能在泛型方法中使用这个Mapper.Map,因为它需要在编译时指定类。

嗯,我可以

  1. 使用 AutoMapper 库
  2. 使用 if 语句预先定义类型(没有检查它是否以这种方式工作)
if (typeof(TA) == typeof(ClassA1)){
    ClassB1 dataB = Mapper.Map(dataA)
}
  1. 使用适配器模式,在这种情况下对我来说看起来很重

尽管如此,我相信我从一开始就做错了,并且存在一些美丽的解决方案。有什么想法吗?

c# design-patterns adapter
1个回答
0
投票

请注意,您的示例混合了单个实例和类列表,但由于其伪代码,我假设这只是一个错误。

从根本上来说,你想要“双重调度”,即选择的函数取决于 2 个参数的类型。

在 C# 中执行此操作的一种方法是使用

dynamic
(这是完整的运行时调度)。如果您将调用映射器的参数转换为(动态),那么要使用哪个映射器函数的“选择”将在运行时而不是编译时确定。

缺点是,如果您忘记将正确的项目添加到映射器,则可能会出现运行时类型错误,就像任何使用动态的东西一样。

如果您可以控制 ClassA1 类型项目,您可以通过回调来实现,即

public interface IMappable<TOutput> //Possible with some type resritcions so you get some ancestior type for you 'B' classes
{
    TOutput Map();
}

public interface ISaveable //Possible with some type resritcions so you get some ancestior type for you 'B' classes
{
    public void SaveTo(Source Source);
}

public static class Mapper{ //You can move this logic directly INTO A if approriate

    public static ClassB1 Map(ClassA1 from){
        return new ClassB1();
    }
  
    public static ClassB2 Map(ClassA2 from){
        return new ClassB2();
    }
}

public class ClassB1 : ISaveable { public void SaveTo(Source source) { source.SaveData(this); } }
public class ClassB2 : ISaveable { public void SaveTo(Source source) { source.SaveData(this); } }

public class ClassA1 : IMappable<ClassB1> //This is your fundemental type mapping
{
    public ClassB1 Map() => Mapper.Map(this);
}

public class ClassA2 : IMappable<ClassB2> //This is your fundemental type mapping
{
    public ClassB2 Map() => Mapper.Map(this);
}

你的来电者看起来像这样(暂时忽略你列出的东西):

void ProcessEntity<TA>(string EntityNameInSourceA, string EntityNameInSourceB) where TA: IMappable<ISaveable>
{
    TA dataA = Source1.GetData<TA>(EntityNameInSourceA);
    ISaveable dataB = dataA.Map();
    dataB.SaveTo(Source2);
}

为什么? 很重要的是,该语言允许“单一调度”,您可以创建一个子类并重写一个方法,这样您就可以仅根据一种类型更改行为(选择的函数)。 通过让此类进行第二次调用,您将获得另一个允许重写的点,从而允许双重调度。

© www.soinside.com 2019 - 2024. All rights reserved.