【问题标题】:One-To-One relationship with nullable foreign keys具有可空外键的一对一关系
【发布时间】:2016-11-19 19:52:53
【问题描述】:

我想在 EF 中创建一对一关系,其中外键可以为空(所以,它可以称为 0..1-to-0..1)

public class ProductInstance
{
    public int Id { get; set; }

    public int SaleId { get; set; }
    public Sale Sale { get; set; }
}


public class Sale
{
    public int Id { get; set; }
    public ProductInstance ProductInstance { get; set; }
}

FluentAPI配置:

modelBuilder.Entity<Sale>()
   .HasOptional(x => x.ProductInstance)
   .WithOptionalPrincipal(x => x.Sale);

但它会在表ProductInstance 中生成两列:

  • Sale_id - 外键
  • SaleId

这里是生成的迁移代码:

 CreateTable(
            "dbo.ProductInstances",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    SaleId = c.Int(nullable: false),
                    Sale_Id = c.Int(),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.Sales", t => t.Sale_Id)
            .Index(t => t.Sale_Id);

我怎样才能只得到一列SaleId,这将是外键?

【问题讨论】:

  • 为什么要同时创建一个与 Sale 实例的关联和一个包含 Sale 实例 ID 的属性?这就是在您的表中创建 2 列的原因。
  • EF 代表实体框架,因此它仅适用于数据作为实体而不是值对象。也许在后面的版本中它也支持值对象。

标签: c# entity-framework one-to-one


【解决方案1】:

EF 不支持 one-to-one 与显式 FK 属性的关联 - 没有 HasForeignKey fluent API,如果您尝试使用 ForeignKey 数据注释解决它,您将在迁移过程中遇到多重异常。

唯一的解决方案是删除ProductInstance.SaleId 属性,以模型结束:

public class ProductInstance
{
    public int Id { get; set; }
    public Sale Sale { get; set; }
}

public class Sale
{
    public int Id { get; set; }
    public ProductInstance ProductInstance { get; set; }
}

和配置:

modelBuilder.Entity<Sale>()
   .HasOptional(x => x.ProductInstance)
   .WithOptionalPrincipal(x => x.Sale)
   .Map(a => a.MapKey("SaleId"));

【讨论】:

    【解决方案2】:

    一对零或一关系:

    当一个表的主键在关系数据库(如 SQL Server)中的另一个表中变为 PK 和 FK 时,就会发生一对零或一关系。

    带有数据注释的一对零或一如下:

    public class ProductInstance
    {
        public int Id { get; set; }
    
        public virtual Sale Sale { get; set; }
    }
    
    
    public class Sale
    {
        [Key]
        [ForeignKey("ProductInstance")]
        public int ProductInstanceId { get; set; }
    
       ///write here other properties
    
        public virtual ProductInstance ProductInstance { get; set; }
    }
    

    Fluent API 的一对零或一如下:

    modelBuilder.Entity<ProductInstance>()
                    .HasOptional(s => s.Sale)
                    .WithRequired(ad => ad.ProductInstance);
    

    在上述情况下,可以在没有Sale 的情况下保存ProductInstance,但如果没有ProductInstance 实体,则无法保存Sale 实体。 请注意,在上述情况下,ProductInstance 不能有多个 Sale

    一对一关系:

    我们不能在需要两端的实体之间配置一对一的关系,这意味着ProductInstance实体对象必须包含Sale实体对象,Sale实体必须包含ProductInstance实体对象以保存它。在 MS SQL Server 中,从技术上讲,一对一的关系是不可能的。但是我们可以配置实体之间的一对一关系,其中两端或至少一端是可选的,如下所示:

    一对一的Data Annotation如下:

    public class Department
    {
        [Key]
        public int DepartmentId { get; set; }
    
        ForeignKey("Person")]
        public int PersonId { get; set; }
    
        public virtual Person Person { get; set; }
    }
    
    
    public class Person
    {
        [Key]
        public int PersonId { get; set; }
    
        [ForeignKey("Department")]
        public int? DepartmentId { get; set; }
    
    
        public virtual Department Department { get; set; }
    }
    

    一对一的 Fluent API 如下:

    modelBuilder.Entity<Person>()
                    .HasOptional(pi => pi.Department)
                    .WithMany()
                    .HasForeignKey(s => s.DepartmentId);
    

    【讨论】:

    • @Alexey Markov,测试我的代码!!如果它按您的预期工作,请将其标记为答案以帮助其他有类似问题的人!
    • 您的代码有效,但在这种情况下,销售需要 ProductInstance,但我不知道。那里的主要问题 - 可为空的外键。 Ivan Stoev 说这不受支持。所以我从我的模型中删除了外键属性并只使用导航属性
    • @Alexey Markov,你错过了重点!!在一对一或一对零关系的情况下,您不需要明确的可为空的外键!外键默认可以为空..根据您的代码销售默认可以为空..运行代码并测试它!!
    • 我理解你。我的问题是我想在我的模型中使用 ForeignKey 属性,以便在不加载实例和使用导航属性的情况下轻松建立关系instance.SaleId = ...。但是我明白了,不可能在一对一中获得明确的可为空的外键。所以,现在我只使用导航属性(不太舒服),现在我不在乎外键字段名称 - SaleId,Sale_id - 无论如何。
    • 很高兴为您效劳!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    相关资源
    最近更新 更多