事实证明,具有给定类型层次结构的 EF 映射很困难

本文关键字:EF 映射 层次结构 类型 事实证明 | 更新日期: 2023-09-27 18:36:48

最初我们使用TPT映射来实现非常简单/常见的层次结构,如下所示:

  • 抽象目的地
  • 抽象目的地:目的地1
  • 抽象目的地:目的地2

映射到表的:

  • 抽象目的地
  • Destination1(PK 是 FK 到 AbstractDestination 表)
  • Destination2(PK 是 FK 到 AbstractDestination 表)

现在我们需要引入一个中间类来添加一些常见但新的功能,但仍保留我们现有的具体类。 我们的层次结构现在如下所示:

  • 抽象目的地(仅限摘要)
  • 抽象目的地:目的地1
  • 抽象目的地:目的地2
  • AbstractDestination: IntermediateDestination (僅限摘要)
  • 摘要目的地:
  • 中间目的地:目的地1
  • 摘要目的地: 中间目的地: 目的地2

显然,在 c# 中,我们不能有一个有条件地继承自 AbstractDestination 或 IntermediateDestination 的 Destinaion1(或 2)类,因此我们实际上最终得到:

  • 抽象目的地(仅限摘要)
  • 抽象目的地:目的地1
  • 抽象目的地:目的地2
  • AbstractDestination: IntermediateDestination (僅限摘要)
  • 摘要目的地: 中间目的地: 目的地3
  • 摘要目的地: 中间目的地: 目的地4

所以最后我们现在有 4 个具体类(或 4 个"目的地"),其中 Destination3 与 Destination1 共享所有相同的属性,Destination4 与 Destination2 共享所有相同的属性。

由于具体目标共享相同的属性,因此我们的DBA希望尽可能重用现有表,并希望表结构看起来像这样(这对我来说确实有意义):

  • 抽象目的地
  • 中间目的地(PK 是 FK 到 AbstractDestination 表)
  • Destination1(PK 是 FK 到 AbstractDestination 表)
  • Destination2(PK 是 FK 到 AbstractDestination 表)

基本上,PK 在所有表之间共享。对于我们的具体类 Destination1,Destinatino2,在 AbstractDestination 表和相应的 DestinatinoX 表中将有一个条目。对于我们的具体类 Destination3,Destinatino4,每个表中都有一个条目。

我还没有找到使用 EF 将此表结构映射回 4 个单独的具体类的方法。

(我更喜欢代码优先的流畅API,但任何映射策略都可以)

可能的实现如下所示:

public abstract class AbstractDestination
{
    public Guid Id { get; set; }
}
public partial class Destination1 : AbstractDestination
{
    public string EmailAddress { get; set; }
}
public partial class Destination2 : AbstractDestination
{
    public string File { get; set; }
}
public abstract class IntermediateDestination : AbstractDestination
{
    public bool Zip { get; set; } //several new persisted properties
    public string ZipKey { get; set; }
}
public partial class Destination3 : IntermediateDestination        
{
    public string EmailAddress { get; set; } //same as Destination1
}
public partial class Destination4 : IntermediateDestination
{
    public string File { get; set; } //same as Destination2
}

事实证明,具有给定类型层次结构的 EF 映射很困难

我不知道

现在我是否已经理解了,但是为什么不对这样的属性使用接口的多重继承呢?

public interface IBlock1 { 
   String Email{ get; set; } 
   Int Guid{ get; set; } 
}
public interface IBlock2 { 
   String Name{ get; set; } 
   Int Number{ get; set; } 
}

然后根据需要以这种方式使用它

public partial class Destination4 : IBlock1 ,IBlock2 
{
}

旧答案

我可以建议你参考装饰器设计模式,它允许你向现有类添加额外的功能

使用它,您可以维护您的 3 个类

  1. 抽象目的地
  2. Destination1(PK 是 FK 到 AbstractDestination 表)
  3. Destination2(PK 是 FK 到 AbstractDestination 表)

类可能与此类似

abstract class AbstractDestination 
{
  public abstract void Operation();
}

混凝土类

class ConcreteAbstractDestination : AbstractDestination 
{
  public override void Operation()
  {
     Console.WriteLine("ConcreteComponent.Operation()");
  }
}

实现AbstractDestination的装饰器抽象类

abstract class Decorator : AbstractDestination
{
  protected AbstractDestination abstractDestination;
  public void SetDestination(AbstractDestination abstractDestination)
  {
    this.abstractDestination= abstractDestination;
  }
  public override void Operation()
  {
     if (abstractDestination != null)
     {
       abstractDestination.Operation();
     }
  }
}

此时,您可以添加 2 个子类Destination1Destination2具有特定行为

class Destination1: Decorator
{
  public override void Operation()
  {
    base.Operation();
    Console.WriteLine("ConcreteDecoratorA.Operation()");
  }
  void NewBehavior()
  {
    //this is a new behavior
  }
}

class Destination2: Decorator
{
  public override void Operation()
  {
    base.Operation();
    AddedBehavior();
    Console.WriteLine("ConcreteDecoratorB.Operation()");
  }
  void AddedBehavior()
  {
  }
}

在 Decorator 类中,您可以在 Destination1Destination2 之间存储新的共享行为,但您可以自由地为每个类添加特定功能。

通过这种方式,您可以维护相同的数据库结构。