具有给定类型层次结构的EF映射证明是困难的

时间:2015-01-07 17:04:12

标签: c# sql .net entity-framework

最初我们使用TPT映射来实现一个非常简单/通用的层次结构:

  • AbstractDestination
  • AbstractDestination:Destination1
  • AbstractDestination:Destination2

映射到表:

  • AbstractDestination
  • Destination1 (PK是FK到AbstractDestination表)
  • Destination2 (PK是FK到AbstractDestination表)

现在我们需要引入一个中间类来添加一些常见但新的功能,但仍然维护我们现有的具体类。我们的层次结构现在看起来像这样:

  • AbstractDestination (仅限摘要)
  • AbstractDestination:Destination1
  • AbstractDestination:Destination2
  • AbstractDestination:IntermediateDestination (仅抽象)
  • AbstractDestination:IntermediateDestination:Destination1
  • AbstractDestination:IntermediateDestination:Destination2

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

  • AbstractDestination (仅限摘要)
  • AbstractDestination:Destination1
  • AbstractDestination:Destination2
  • AbstractDestination:IntermediateDestination (仅抽象)
  • AbstractDestination:IntermediateDestination:Destination3
  • AbstractDestination:IntermediateDestination:Destination4

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

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

  • AbstractDestination
  • IntermediateDestination (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
}

1 个答案:

答案 0 :(得分:0)

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

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 
{

}

旧回答

我建议您参考Decorator Design pattern,以便为现有课程添加额外的功能

使用它可以保持3个班级

  1. AbstractDestination
  2. Destination1(PK是FK到AbstractDestination表)
  3. Destination2(PK是FK到AbstractDestination表)
  4. 这些类可能与此类似

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

    具体类

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

    实现AbstractDestination

    的Decorator Abstract类
    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之间存储新的共享行为,但您可以自由地为每个类添加特定功能。

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