【问题标题】:EF Core: how to share foreign key properties between several zero/one-to-many relationshipsEF Core:如何在几个零/一对多关系之间共享外键属性
【发布时间】:2017-05-01 07:42:08
【问题描述】:

所以我正在深入研究 EF Core 并尝试继承和 TPH 模式(我之前没有这方面的经验)。 EF 创建的结果数据库不是我所期望的,我想知道是否有可能使用 fluent-api 获得我正在寻找的结果,或者我是否完全错过了这一点。

首先,这是我的 POCO 课程:

public class Commission
{
    public int Id { get; set; }
    public string Description { get; set; }
    public double Rate { get; set; }
}

public class Party
{
    public int PartyId { get; set; }
    public string Name { get; set; }
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

public class Agency : Party
{
    public string AgencyCode { get; set; }
    public ICollection<Commission> Commissions { get; set; }
}

public class Carrier : Party
{
    public string CarrierCode { get; set; }
    public ICollection<Commission> Commissions { get; set; }
}

public class Principal : Party
{
    public string Website { get; set; }
    public string DistrictCode { get; set; }
}

还有我的上下文类以防万一:

public class PartyContext : DbContext
{
    public DbSet<Agency> Agencies { get; set; }
    public DbSet<Carrier> Carriers { get; set; }
    public DbSet<Party> Parties { get; set; }
    public DbSet<Commission> Commissions { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=LAPTOP-PC\SQLEXPRESS;Database=MyDatabase;Trusted_Connection=True;");
    }
}

基本上,Agency、Carrier 和 Principal 都继承自 Party,因此它们应该具有与 Party 相同的所有属性。 Agency 和 Carrier 具有额外的特定属性,并且应该与 Commissions 具有零或一对多的关系。此外,Principal 有几个特定属性,但与佣金无关。

生成的数据库如下:

我对 Party 表本身的输出没有任何疑问,并且了解鉴别器字段是什么,但是我不了解它创建的两个外键关系:

  • 委托方_AgencyPartyId
  • 佣金方_CarrierPartyId

我的问题是为什么我不能只在后端拥有一个从 Party 到 Commissions_PartyId 的外键关系?如果可以,我如何告诉 EF 以这种方式创建它?

编辑

使用 Dmitry 的建议并使用 [InverseProperty] 属性,我最终得到了以下数据库设计,这不是所需的输出:

它实际上做了第三个字段(PartyId1)。所以我再次开始查看 EF documentation on relationships 并开始使用不同的注释。使用 [ForeignKey("PartyId")] 属性给了我一些希望,因为它产生了我所期待的设计:

不过,这也产生了一些意想不到的效果。在尝试使用代理商和运营商填充数据库后,我收到了异常。

这是填充代码:

PartyContext _context = new PartyContext();
        // Add an Agency
        var agencyCommission1 = new Commission
        {
            Description = "Contract",
            Rate = 0.075
        };

        var agencyCcommission2 = new Commission
        {
            Description = "Hauling",
            Rate = 0.10
        };

        var agencyCommissionList = new List<Commission>
        {
            agencyCommission1, agencyCcommission2
        };

        var agency = new Agency
        {
            Name = "Agency International",
            Address1 = "12345 Main Street",
            Address2 = "Suite 100",
            City = "Chicago",
            State = "IL",
            Zip = "31202",
            AgencyCode = "AI",
            Commissions = agencyCommissionList
        };

        // Add Carrier
        var carrierCommission1 = new Commission
        {
            Description = "Coal",
            Rate = 0.15
        };

        var carrierCommission2 = new Commission
        {
            Description = "Mining",
            Rate = 0.115
        };

        var carrierCommissionList = new List<Commission>
        {
            carrierCommission1, carrierCommission2
        };

        var carrier = new Carrier
        {
            Name = "Carrier International",
            Address1 = "54321 Main Street",
            Address2 = "Suite 300",
            City = "Cleveland",
            State = "OH",
            Zip = "44115",
            CarrierCode = "CI",
            Commissions = carrierCommissionList
        };

        _context.Agencies.Add(agency);
        _context.Carriers.Add(carrier);

        try
        {
            _context.SaveChanges();
        }
        catch(Exception ex)
        {
            return;
        }

添加代理时的异常是“无法将'EFTest.Agency'类型的对象转换为'EFTest.Carrier'类型。”尝试添加运营商时的异常是“无法将'EFTest.Carrier'类型的对象转换为'EFTest.Agency'类型。”

我要补充一点,当使用原始 EF 设计时,程序确实可以按预期运行,但是额外的字段和外键让我的强迫症有点疯狂:) 欢迎更多想法!

【问题讨论】:

    标签: c# entity-framework inheritance entity-framework-core


    【解决方案1】:

    试试这个:

    public class Commission
    {
        public int Id { get; set; }
        public string Description { get; set; }
        public double Rate { get; set; }
    
        public Party Party { get; set; } // <-- "parent" party link here
    }
    
    public class Agency : Party
    {
        public string AgencyCode { get; set; }
    
        [InverseProperty("Party")] // <-- link this collection with Party.Party
        public ICollection<Commission> Commissions { get; set; }
    }
    
    public class Carrier : Party
    {
        public string CarrierCode { get; set; }
    
        [InverseProperty("Party")] // <-- link this collection with Party.Party
        public ICollection<Commission> Commissions { get; set; }
    }
    

    【讨论】:

    • 感谢您的建议,但这似乎增加了第三列。我最终得到了 AgencyPartyId、CarrierPartyId 和 PartyId1。我会尽快用我尝试过的各种方法来更新我的问题。
    • 这不会像预期的那样工作,导航属性只能参与一种关系,因此Commission.Party 的倒数将是Agency.CommisionsCarrier.Commisions,而不是两者。
    【解决方案2】:

    如果您将两个关系配置为使用与外键相同的属性,您仍然有两个关系。因此,当添加 CommissionPartyId 等于 1 时,EF 会将其解释为与 Agency 相关,PartyId 等于 1 CarrierPartyId 等于1,显然这是不可能的。

    您需要在CommissionParty 之间创建关系,但这意味着Commissions 导航属性也需要移动到Party。但是您仍然可以将其隐藏在 Principal 和其他派生类中,方法是使其受到保护并仅在 AgencyCarrier 上公开它:

    public class PartyContext : DbContext
    {
        public PartyContext(DbContextOptions options)
            : base(options)
        {
        }
    
        public DbSet<Agency> Agencies { get; set; }
        public DbSet<Carrier> Carriers { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Party>().HasMany(typeof(Commission), "Commissions").WithOne();
        }
    }
    
    public class Party
    {
        public int PartyId { get; set; }
        protected ICollection<Commission> Commissions { get; set; }
    }
    
    public class Agency : Party
    {
        public new ICollection<Commission> Commissions
        {
            get { return base.Commissions; }
            set { base.Commissions = value; }
        }
    }
    
    public class Carrier : Party
    {
        public new ICollection<Commission> Commissions
        {
            get { return base.Commissions; }
            set { base.Commissions = value; }
        }
    }
    
    public class Commission
    {
        public int Id { get; set; }
    }
    

    【讨论】:

    • 好的,这样我在佣金表中仍然有两个关系,PartyId 和 PartyId1。只是想确保这就是您要解释的内容。我确实喜欢您通过明确公开该属性所做的事情 - 这对我来说是新的!谢谢
    • @FranksBrain 也许您需要升级到更新的版本。我在夜间构建中尝试了这个,只创建了一个 FK。
    • 我还有一个公共派对 { get;放; } 在佣金表里面,呵呵!现在可以了,谢谢你的澄清!
    • 不幸的是,我在核心 2.1 中无法正常工作。我的列仍然在佣金表中生成为 AgencyId 和 CarrierId,而不是 PartyId
    • @AndrewCetinic 我刚刚检查过,在 2.1 中,我提供的代码仅导致 Commission 表中的 PartyId
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-16
    • 2019-07-25
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    相关资源
    最近更新 更多