一对零或一关系,其中 FK 是从属表上的复合 PK 的一部分

One-to-Zero-or-One relationship where the FK is part of a compoun PK on dependent tables

我正在用 Entity Framework 6.1.2 为这两个 table 开发一个库:

CREATE TABLE [dbo].[CODES]
(
    [CODE] [nvarchar](20) NOT NULL,
    [ ... ],
    CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE] ASC
    )
)

CREATE TABLE [dbo].[AGGREGATIONS]
(
    [ID_AGGREGATION] INT IDENTITY(1,1) NOT NULL, 
    [CODE_LEVEL] TINYINT NOT NULL, 
    [CODE] NVARCHAR(20) NOT NULL,
    [ ... ], 
    CONSTRAINT [PK_AGGREGATIONS] PRIMARY KEY CLUSTERED
    (
        [ID_AGGREGATION] ASC
    ),
    CONSTRAINT [FK_AGGREGATIONS_CODES] FOREIGN KEY ([CODE]) REFERENCES [dbo].[CODES] ([CODE])
)

关系:

CODES 可以有零个或一个 AGGREGATIONS,但是 AGGREGATIONS 会有一个 CODES

为此,我有这两个实体 类:

public class CODES
{
    public string CODE { get; set; }
    [ ... ]

    public virtual AGGREGATIONS Aggregation { get; set; }
    public virtual AGGREGATION_CHILDS AggregationChild { get; set; }
}
public class AGGREGATIONS
{
    public int ID_AGGREGATION { get; set; }
    public string CODE { get; set; }
    public byte CODE_LEVEL { get; set; }

    public virtual CODES Code { get; set; }
}

我试过在聚合的 EntityTypeConfiguration<AGGREGATIONS>:

上设置 AGGREGATIONSCODES 之间的关系
HasKey(ag => ag.ID_AGGREGATION);
HasRequired(ag => ag.Code).WithOptional(c => c.Aggregation);

但是我不知道如何将AGGREGATIONS.CODE设置为这个关系中的外键。

如何设置这段关系的FK?

更新

我需要使用 AGGREGATIONS.ID_AGGREGATION 因为还有另一个 table 有一个到 AGGREGATIONSCODES 的外键 tables:

CREATE TABLE [dbo].[AGGREGATION_CHILDS]
(
    [ID_AGGREGATION] [int] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [POSITION] [int] NOT NULL,
    CONSTRAINT [PK_AGGREGATION_CHILDS] PRIMARY KEY CLUSTERED 
    (
        [ID_AGGREGATION] ASC,
        [CODE] ASC
    ), 
    CONSTRAINT [FK_AGGREGATION_CHILDS_AGGREGATIONS] FOREIGN KEY ([ID_AGGREGATION]) REFERENCES [AGGREGATIONS]([ID_AGGREGATION]), 
    CONSTRAINT [FK_AGGREGATION_CHILDS_CODES] FOREIGN KEY ([CODE]) REFERENCES [CODES]([CODE])

如果我按照您在 中的建议进行操作,我将在 AGGREGATION_CHILDS table 中有两列 CODE。其中之一将是 AGGREGATIONS table 和 CODES table 的外键。而另一个FK到CODES table.

我想你快到了。对于可选的 Codes.Aggregation 尝试:

HasRequired(ag => ag.Code).WithOptional(c => c.Aggregation); 

或在您的上下文中 class:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Aggregations>()
                .HasRequired(a => a.Code)
                .WithOptional(c => c.Aggregation);
}

在一对一关系中,EF 要求依赖实体的键也必须是主体的外键,因此,您的模型将是这样的:

public class CODES
{
  [Key]
  public string CODE { get; set; }
  [ ... ]

  public virtual AGGREGATIONS Aggregation { get; set; }
}

public class AGGREGATIONS
{
   [Key, ForeignKey("Codes")]
   public string CODE { get; set; }
   public byte CODE_LEVEL { get; set; }

   public virtual CODES Code { get; set; }
}

如果您需要使用 Fluent Api,我想您可以在 Aggregations 的配置 class 中执行此操作:

HasKey(ag=>ag.Code);
HasRequired(ag => ag.Code).WithOptional(c => c.Aggregation);

您可以在此 page 中找到更多信息。

更新 1

为了实现你想要的,由于我在上面评论的限制,你不能在依赖实体中声明 FK 属性。所以你的模型会是这样的:

public class CODES
{
  [Key]
  public string CODE { get; set; }

  [Required]
  public virtual AGGREGATIONS Aggregation { get; set; }

  public ICollection<AGGREGATION_CHILDS> AggregationChilds { get; set; }
}
public class AGGREGATIONS
{
   [Key]
   public int ID_AGGREGATION { get; set; }

   public byte CODE_LEVEL { get; set; }

   public virtual CODES Code { get; set; }

   public ICollection<AGGREGATION_CHILDS> AggregationChilds { get; set; }

}

public class AGGREGATION_CHILDS
{
   [Key,Column(Order = 0),ForeignKey("Code")]
   public string CODE { get; set; }

   [Key,Column(Order = 1),ForeignKey("Aggregation")]
   public int ID_AGGREGATION { get; set; }

   public virtual CODES Code { get; set; }

   public virtual AGGREGATIONS Aggregation { get; set; }
}

如果您不想使用数据注释,您可以删除所有属性并在配置中使用 Fluent Api 指定相同的属性 classes:

 public class CodesMap : EntityTypeConfiguration<CODES>
 {
    public CodesMap()
    {
        // Primary Key
        this.HasKey(t => t.CODE);
    }
 }

 public class AggregationsMap : EntityTypeConfiguration<AGGREGATIONS>
 {
    public AggregationsMap()
    {
        // Primary Key
        this.HasKey(t => t.ID_AGGREGATION);
        HasRequired(ag => ag.Code).WithOptional(c => c.Aggregation);
    }
 }

 public class AggregationChildsMap : EntityTypeConfiguration<AGGREGATIONS_CHILDS>
 {
    public AggregationChildsMap()
    {
        // Primary Key
        this.HasKey(t => new{t.CODE,t.ID_AGGREGATION});
        HasRequired(t => t.Code).WitMany(c => c.AggregationChilds).HasForeignKey(t=>t.CODE);
        HasRequired(t => t.Aggregation).WitMany(ag => ag.AggregationChilds).HasForeignKey(t=>t.ID_AGGREGATION);

    }
 }

更新 2

无法在 AGGREGATION_CHILDS 中指定复合 PK 并配置此实体与 AGGREGATIONS 之间的一对一关系,除非复合键是 [=16= 中的键] 也。如果要创建一对一关系并在依赖实体中指定 FK 属性,则该 FK 也必须是 PK,因此两个实体必须共享相同的 PK。如果您在该依赖实体中声明另一个键,FK 与另一个关系相关,EF 会期望这两个关系应该是一对多的(如我在更新 1 中显示的示例)。如果您尝试创建两个一对一关系,并尝试将依赖端的 PK 与主体的 PK 进行复合,那么您将收到此异常:

AGGREGATION_CHILDS_Aggregation_Source: : Multiplicity is not valid in Role 'AGGREGATION_CHILDS_Aggregation_Source' in relationship 'AGGREGATION_CHILDS_Aggregation'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.

AGGREGATION_CHILDS_Code_Source: : Multiplicity is not valid in Role 'AGGREGATION_CHILDS_Code_Source' in relationship 'AGGREGATION_CHILDS_Code'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.

我是这样解决问题的:

public class CODES
{
    public string CODE { get; set; }
    public byte CODE_LEVEL { get; set; }
    [ ... ]

    public virtual AGGREGATION_CHILDS AggregationChild { get; set; }
    public virtual AGGREGATIONS Aggregation { get; set; }
}
public class AGGREGATIONS
{
    public string CODE { get; set; }
    public string CREATED { get; set; }

    public virtual ICollection<AGGREGATION_CHILDS> AggregationChilds { get; set; }
    public virtual CODES Code { get; set; }
}
public class AGGREGATION_CHILDS
{
    public string CODE { get; set; }
    public string PARENT_CODE { get; set; }
    public int POSITION { get; set; }

    public AGGREGATIONS Aggregation { get; set; }
    public CODES Code { get; set; }
}

以及他们的地图:

class AGGREGATIONSConfiguration : EntityTypeConfiguration<AGGREGATIONS>
{
    public AGGREGATIONSConfiguration()
    {
        HasKey(ag => ag.CODE);

        Property(ag => ag.CODE)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        Property(ag => ag.CODE)
            .HasMaxLength(20)
            .IsRequired();

        Property(ag => ag.CREATED)
            .HasMaxLength(50)
            .IsOptional();

        HasRequired(ag => ag.Code)
            .WithOptional(c => c.Aggregation)
            .WillCascadeOnDelete(false);
    }
}
class AGGREGATION_CHILDSConfiguration : EntityTypeConfiguration<AGGREGATION_CHILDS>
{
    public AGGREGATION_CHILDSConfiguration()
    {
        HasKey(ag_ch => ag_ch.CODE);

        Property(ag_ch => ag_ch.CODE)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

        Property(ag_ch => ag_ch.CODE)
            .HasMaxLength(20)
            .IsRequired();

        Property(ag_ch => ag_ch.PARENT_CODE)
            .HasMaxLength(20)
            .IsRequired();

        HasRequired(ag_ch => ag_ch.Aggregation)
            .WithMany(ag => ag.AggregationChilds)
            .HasForeignKey(ag_ch => ag_ch.PARENT_CODE);

        HasRequired(ag_ch => ag_ch.Code)
            .WithOptional(c => c.AggregationChild)
            .WillCascadeOnDelete(false);
    }
}

CODESConfiguration 与此处无关。

我在 AGGREGATIONSAGGREGATION_CHILDS 上将 CODE 用作 PK,还用作 FKCODES table。 我已从 AGGREGATIONS table 中删除 ID_AGGREGATION 并在 AGGREGATION_CHILDS table.

上删除复合 PK

我希望它能帮助那些对一对零或一关系有同样问题的人。