如何在实体框架中定义除默认方法之外的外键关系

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在实体框架中定义除默认方法之外的外键关系相关的知识,希望对你有一定的参考价值。

我有一个问题,我试图用Entity Framework 6.0解决,希望有人在这里可以给出一些指导。我对ADO.NET更加满意,但想在EF中完成这个项目。

我有一个名为Policy的对象,另一个名为PayPlan

public class Policy
{
    //Various properties not relevant

    public PayPlan PaymentPlan { get; set; }
}

public class PayPlan
{
     public int PayPlanId { get; set;}

     public string Description { get; set; }
}

如您所见,在此示例中,PayPlan是Policy的子对象。它可能为null,或者可能存在与策略关联的PayPlan的单个实例。

当我运行模型构建器时,它会创建表并将外键插入到PayPlan中记录的策略表中。这对我来说真的不起作用,因为1)我想保持Db架构类似于应用程序的先前版本,其中PolicyId是进入Pay​​Plan的ForeignKey和2)使用级联删除,如果PayPlan被删除它我会采用这个政策,我需要这个反过来。策略是根对象形式,Db中的所有其他对象绘制它们的关系。 PayPlan,顺便说一句,只是这个讨论的一个例子,但在实际项目中,Policy对象将以类似的方式包含许多与之关联的子对象。

我的问题是,如何通过数据注释或Fluent API进行设置,以实现我描述的模式?

答案

所以,在你提到你正在使用该版本之后,在EF 6上挖掘了一下这个,并发现了这个:

显然,EF 6不支持备用密钥。作为@rowanmiller在此Github问题上:

不幸的是,这是EF6的限制。您不能以一对一的关系拥有外键属性,除非它也是主键属性。这主要是因为EF6不支持备用键/唯一索引,因此您无法强制非主键属性是唯一的。当外键属性不在实体中时你可以这样做的事实有点怪癖......但显然不是我们要删除的东西:smile:。

EF Core支持BTW备用密钥(因此也支持此方案)。

Mapping foreign key in HasOptional().WithOptionalDependent() relation

你仍然可以拥有你想要的FK,但你不能在你的PayPlan类上拥有FK属性。如果你这样做,你最终会得到两个FK。所以,如果你像这样配置你的关系:

public class Policy
{
    public int PolicyId { get; set; }

    public string Description { get; set; }

    public PayPlan PaymentPlan { get; set; }
}

public class PayPlan
{
    public int PayPlanId { get; set; }
    public string Description { get; set; }
    public Policy Policy { get; set; }
}


protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<PayPlan>()
        .HasOptional(a => a.Policy)
        .WithOptionalDependent(p => p.PaymentPlan)
        .WillCascadeOnDelete(true);
}

你将在SQL上以此结束:

enter image description here

因为我从来没有这种情况,所以不知道这件事。很糟糕。但你仍然可以使用EF核心:),这很酷。

EF Core只是为了记录而回答

您也可以使用FluentAPI解决此问题。 (我更喜欢FluentApi而不是使用Attributes污染我的模型)。此外,由于您没有提到您正在使用的EF版本,我假设您使用的是EF Core。

public class Policy
{
    public int PolicyId { get; set; }

    public string Description { get; set; }

    public PayPlan PaymentPlan { get; set; }
}

public class PayPlan
{
    public int PayPlanId { get; set; }
    public string Description { get; set; }

    public Policy Policy { get; set; }
    public int? PolicyId { get; set; }
}

上下文类:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Policy>()
        .HasOne(a => a.PaymentPlan)
        .WithOne(b => b.Policy)
        .IsRequired(false)
        .HasForeignKey<PayPlan>(b => b.PolicyId)
        .OnDelete(DeleteBehavior.Cascade);
}

这将在SQL上生成以下表:

enter image description here

另一答案

如果我正确理解了您的要求,您希望构建如下模型:

public class Policy {
    [Key]
    public int PolicyId { get; set; }
    // this attribute is not required, but I prefer to be specific
    // this attribute means navigation property PaymentPlan
    // is "anoter end" of navigation property PayPlan.Policy
    [InverseProperty("Policy")]
    public virtual PayPlan PaymentPlan { get; set; }
}

public class PayPlan {
    [Key]
    public int PayPlanId { get; set; }
    // define foreign key explicitly here
    [ForeignKey("Policy")]
    public int PolicyId { get; set; }

    public virtual Policy Policy { get; set; }

    public string Description { get; set; }
}

更新:上述内容适用于EF Core,但在EF 6中不起作用.EF 6将此视为一对多关系(并且正确,因为一个策略可能有多个PayPlans)。要创建一个到(零或)一个关系,您可以创建如下模型:

public class Policy
{
    [Key]
    public int PolicyId { get; set; }                
    [InverseProperty("Policy")]
    public virtual PayPlan PaymentPlan { get; set; }
}

public class PayPlan
{
    [Key, ForeignKey("Policy")]
    public int PolicyId { get; set; }        

    public Policy Policy { get; set; }

    public string Description { get; set; }
}

因此,PayPlan没有自己的Id,而是具有PK和FK的PolicyId。这样,一个策略可能只存在一个(或没有)薪酬计划。

以上是关于如何在实体框架中定义除默认方法之外的外键关系的主要内容,如果未能解决你的问题,请参考以下文章

如何从使用实体框架的外键链接的多个表中获取所有数据?

理解实体框架核心外键关系

使用实体框架,如何在两个模型上添加外键以相互引用

如何在 1:m 关系中设置默认值,Django 中 user_auth_model 的外键

实体框架添加重复的外键

实体框架 4.1 代码优先外键 ID