【问题标题】:Is it possible to set up a navigation property without the foreign key?是否可以在没有外键的情况下设置导航属性?
【发布时间】:2019-07-11 22:47:59
【问题描述】:

是否可以有两个(或多个)相同类型的导航属性?

我的模型看起来像这样......

public class Agreement
{
    public int Id { get; set; }
    public Guid? BuyerId { get; set; }
    public Guid? SellerId { get; set; }

    public AgreementInfo ByerAgreementInfo { get; set; }
    public AgreementInfo SellerAgreementInfo { get; set; }
}

public class AgreementInfo
{
    // PK is AgreementId and OwnerActorId combined.
    public int AgreementId { get; set; }
    public Guid OwnerActorId { get; set; }
    public string Name { get; set; }
}

...我正在尝试通过匹配 AgreementId 和 ByerId/SellerId 来包含导航属性...

modelBuilder.Entity<Agreement>().HasOne(x => x.ByerAgreementInfo).WithOne().HasForeignKey<Agreement>(x => new {x.Id, x.ProviderId});
modelBuilder.Entity<Agreement>().HasOne(x => x.SellerAgreementInfo).WithOne().HasForeignKey<Agreement>(x => new { x.Id, x.RequesterId });

...但这会导致循环依赖。

有什么方法可以在不使用外键的情况下包含这些属性?或者是否有其他解决方案(除了向信息表添加 id 列)允许我将信息表行用作协议类中的导航属性?

【问题讨论】:

  • 检查我的答案。它应该适合你。

标签: c# .net entity-framework .net-core entity-framework-core


【解决方案1】:

...但这会导致循环依赖

是的!它会。为了克服这个问题,您必须在 Fluent API 配置中指定 .OnDelete(DeleteBehavior.Restrict);,如下所示,但首先您还必须编写 Agreement 模型类,如下所示:

public class Agreement
{
    public int Id { get; set; }
    public Guid? BuyerId { get; set; }
    public Guid? SellerId { get; set; }

    public int AgreementIdForBuyer { get; set; }
    public Guid OwnerActorIdForBuyer { get; set; }

    public int AgreementIdForSeller { get; set; }
    public Guid OwnerActorIdForSeller { get; set; }

    public AgreementInfo ByerAgreementInfo { get; set; }
    public AgreementInfo SellerAgreementInfo { get; set; }
}

现在在 Fluent API 配置中:

modelBuilder.Entity<Agreement>()
    .HasOne(x => x.ByerAgreementInfo)
    .WithOne()
    .HasForeignKey<Agreement>(p => new {p.AgreementIdForBuyer, p.AgreementIdForBuyer})
    .OnDelete(DeleteBehavior.Restrict); // <-- Here it is

modelBuilder.Entity<Agreement>()
    .HasOne(x => x.SellerAgreementInfo)
    .WithOne()
    .HasForeignKey<Agreement>(p => new {p.AgreementIdForSeller, p.OwnerActorIdForSeller})
    .OnDelete(DeleteBehavior.Restrict); // <-- Here it is

【讨论】:

    【解决方案2】:

    有多种方法可以实现您想要的。您可以在您的孩子上使用带有InverseProperty 属性的Data Annotations,在您的父级上使用ForeignKey 属性,或者在您的模型构建器中使用流畅的语法。我倾向于尽可能使用属性(Data Annotations)(这只是我个人的喜好),我发现它需要查看实际模式本身的关系(尽管其他人可能不会)。

    使用ForeignKey属性:

    public class Agreement
    {
        ...
    
        [ForeignKey("ByerAgreementInfo ")]
        public int ByerAgreementInfoId { get; set; }
        public AgreementInfo ByerAgreementInfo { get; set; }
    
        [ForeignKey("SellerAgreementInfo ")]
        public int SellerAgreementInfoId { get; set; }
        public AgreementInfo SellerAgreementInfo { get; set; 
    }
    

    使用InverseProperty属性:

    public class AgreementInfo
    {
    
        ...
    
        [InverseProperty("ByerAgreementInfo ")]
        public ICollection<Agreement> Sellers { get; set; }
        [InverseProperty("SellerAgreementInfo ")]
        public ICollection<Agreement> Buyers { get; set; }
    }
    

    如果你想使用Fluent Syntax,我相信下面的方法会起作用(虽然我已经有一段时间没用过了):

    modelBuilder.Entity<Agreement>()
        .HasOne(x => x.ByerAgreementInfo)
        .WithOne()
        .HasForeignKey<Agreement>(p => p.ByerAgreementInfoId);
    
    modelBuilder.Entity<Agreement>()
        .HasOne(x => x.SellerAgreementInfo)
        .WithOne()
        .HasForeignKey<Agreement>(p => p.SellerAgreementInfoId);
    

    注意:我想你可能需要模型中的实际 ID,虽然我不记得了。

    【讨论】:

      猜你喜欢
      • 2019-06-16
      • 1970-01-01
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-29
      相关资源
      最近更新 更多