事实证明,具有给定类型层次结构的 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
}
现在我是否已经理解了,但是为什么不对这样的属性使用接口的多重继承呢?
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 个类
- 抽象目的地
- Destination1(PK 是 FK 到 AbstractDestination 表)
- 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 个子类Destination1
并Destination2
具有特定行为
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 类中,您可以在 Destination1
和 Destination2
之间存储新的共享行为,但您可以自由地为每个类添加特定功能。
通过这种方式,您可以维护相同的数据库结构。