具有可空外键的一对一关系

5
我希望在EF中创建一对一关系,其中外键可以为null(因此可以称为0..1至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 的外键?


1
为什么要创建一个与Sale实例相关联的关联和一个保存Sale实例Id的属性?这就是你的表中创建2个列的原因。 - Frederik Gheysels
EF代表实体框架,因此它只能处理数据作为实体而不是值对象。也许在后续版本中它也支持值对象。 - Mohamadreza
2个回答

8

EF不支持使用显式外键属性的one-to-one关联 - 没有HasForeignKey流畅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"));

7

一对零或一关系:

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

使用数据注释实现一对零或一关系的方法如下:

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

在上述情况中,可以保存一个没有销售记录的“ProductInstance”,但是不能保存没有“ProductInstance”的“Sale”实体。需要注意的是,在该情况下,“ProductInstance”不能有多个销售记录。

一对一关系:

我们无法在两个端都必须存在的实体之间配置一对一关系,这意味着“ProductInstance”实体对象必须包含“Sale”实体对象,并且“Sale”实体必须包含“ProductInstance”实体对象才能保存它。在 MS SQL Server 中技术上不可能实现一对一关系。但是,我们可以按以下方式在两个端或至少一个端是可选的实体之间配置一对一关系:

使用数据注释配置一对一关系如下:

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,请测试我的代码!如果它按照你的预期工作,请将其标记为答案,以帮助其他遇到类似问题的人! - TanvirArjel
你的代码可以运行,但在这种情况下,销售需要产品实例,但我不知道。主要问题在于可空外键。Ivan Stoev说这不受支持。因此,我从我的模型中删除了外键属性,只使用导航属性。 - Alexey Markov
@Alexey Markov,您忽略了一个重要的点!!在一对一或一对零的关系中,您不需要显式地使用可空外键!!外键默认是可空的...根据您的代码,sale默认是可空的...运行代码并测试它吧!! - TanvirArjel
我理解你的问题。我的问题是,我想在我的模型中使用ForeignKey属性轻松地建立关系,例如 instance.SaleId = ...,而不必加载实例并使用导航属性。但是我发现,在一对一关系中无法显式获取可空外键。因此,现在我仅使用导航属性(稍微不太方便),现在我不再关心外键字段名称 - SaleId、Sale_id - 无论哪种都可以。 - Alexey Markov
很高兴能帮助你! - TanvirArjel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接