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

时间:2015-03-12 13:53:06

标签: c# entity-framework fluent

我正在为这两个表开发一个包含Entity Framework 6.1.2的库:

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; }
}

我试过这个来设置AGGREGATIONSCODES之间关于聚合的关系&#39; EntityTypeConfiguration<AGGREGATIONS>

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

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

如何在这种关系上设置FK?

更新

我需要使用AGGREGATIONS.ID_AGGREGATION,因为还有另一个表具有AGGREGATIONSCODES表的外键:

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])

如果我在answer中按照您的建议行事,我会在CODE表格中有两列AGGREGATION_CHILDS。其中一个将是AGGREGATIONS表的外键,也是CODES表的外键。而另一个FK到CODES表。

3 个答案:

答案 0 :(得分:2)

在一对一关系中,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的配置类中执行此操作:

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指定相同的属性:

 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之间配置一对一关系,除非复合键也是AGGREGATIONS中的键。 。如果要创建一对一关系并在dependend实体中指定FK属性,那么该FK也必须是PK,因此两个实体必须共享相同的PK。如果在该dependend实体中声明另一个Key,其中FK与另一个关系相关,那么EF会期望这两个关系应该是一对多的(就像我在Update 1中显示的那样)。如果您尝试创建两个一对一关系,并尝试将从属端的PK与主体的PK复合,那么您将收到此异常:

  

AGGREGATION_CHILDS_Aggregation_Source ::多重性无效   关系中的角色'AGGREGATION_CHILDS_Aggregation_Source'   'AGGREGATION_CHILDS_Aggregation'。因为依赖角色   属性不是关键属性,是上限   依赖角色的多样性必须为'*'。

     

AGGREGATION_CHILDS_Code_Source ::多重性在角色中无效   'AGGREGATION_CHILDS_Code_Source'的关系   'AGGREGATION_CHILDS_Code'。因为依赖角色属性是   不是关键属性,多重性的上限   依赖角色必须是'*'。

答案 1 :(得分:0)

我认为你几乎就在那里。对于可选的Codes.Aggregation尝试:

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

或在您的上下文类中:

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

答案 2 :(得分:0)

这就是我解决问题的方法:

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与此无关。

我在CODEAGGREGATIONS以及 FK 上使用AGGREGATION_CHILDS作为 PK CODES表。 我已从ID_AGGREGATION表格移除AGGREGATIONS,并在AGGREGATION_CHILDS表格中移除了复合 PK

我希望它可以帮助那些在“一对一”或“一对一”关系中遇到同样问题的人。