【问题标题】:Entity Framework Core with Multiple Foreign Key On Same Column同一列上具有多个外键的实体框架核心
【发布时间】:2019-02-23 07:04:59
【问题描述】:

我有 3 个表与同一个 TransactionLog.DocumentId 列有关系。 我用 DocumentTypeId 区分外键:

1 - 发票, 2 - 借记单, 3 - CreditNote

我为实体搭建脚手架:

public partial class TransactionLog
{
    public int TransactionLogId { get; set; }
    public int? DocumentId { get; set; }
    public int? DocumentTypeId { get; set; }
    public decimal? Amount { get; set; }

    public CreditNote CreditNote { get; set; }
    public Invoice Invoice { get; set; }
    public DebitNote DebitNote { get; set; }
}

public partial class Invoice
{
    public Invoice()
    {
        TransactionLog = new HashSet<TransactionLog>();
    }

    public int InvoiceId { get; set; }
    public string InvoiceNumber { get; set; }
    public decimal Amount { get; set; }

    public ICollection<TransactionLog> TransactionLog { get; set; }
}

public partial class DebitNote
{
    public DebitNote()
    {
        TransactionLog = new HashSet<TransactionLog>();
    }

    public int DebitNoteId { get; set; }
    public string DebitNoteNumber { get; set; }
    public decimal Amount { get; set; }

    public ICollection<TransactionLog> TransactionLog { get; set; }
}

public partial class CreditNote
{
    public CreditNote()
    {
        TransactionLog = new HashSet<TransactionLog>();
    }

    public int CreditNoteId { get; set; }
    public string CreditNoteNumber { get; set; }
    public decimal Amount { get; set; }

    public ICollection<TransactionLog> TransactionLog { get; set; }
}

我想在 Invoice、DebitNote 和 CreditNote 表中分别插入 1 条记录,并在 TransactionLog 中为每笔交易插入 3 条记录。

这是我的代码:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<CreditNote>(entity =>
        {
            entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");

            entity.Property(e => e.CreditNoteNumber)
                .HasMaxLength(50)
                .IsUnicode(false);
        });

        modelBuilder.Entity<DebitNote>(entity =>
        {
            entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");

            entity.Property(e => e.DebitNoteNumber)
                .HasMaxLength(50)
                .IsUnicode(false);
        });

        modelBuilder.Entity<Invoice>(entity =>
        {
            entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");

            entity.Property(e => e.InvoiceNumber)
                .HasMaxLength(50)
                .IsUnicode(false);
        });

        modelBuilder.Entity<TransactionLog>(entity =>
        {
            entity.Property(e => e.Amount).HasColumnType("decimal(18, 4)");

            entity.HasOne(d => d.CreditNote)
                .WithMany(p => p.TransactionLog)
                .HasForeignKey(d => d.DocumentId)
                .HasConstraintName("FK_TransactionLog_CreditNote");

            entity.HasOne(d => d.DebitNote)
                .WithMany(p => p.TransactionLog)
                .HasForeignKey(d => d.DocumentId)
                .HasConstraintName("FK_TransactionLog_DebitNote");

            entity.HasOne(d => d.Invoice)
                .WithMany(p => p.TransactionLog)
                .HasForeignKey(d => d.DocumentId)
                .HasConstraintName("FK_TransactionLog_Invoice");
        });
    }

但是,DocumentId 没有保存正确的 InvoiceId、CreditNoteId、DebitNoteId。我用 SQL Profiler 检查,它总是会得到 3 个插入的第一个 scope_identity(),在我的例子中是 CreditNoteid。

知道如何从 Invoice、CreditNote 和 DebitNote 中获取正确的 ID 吗? 或者我不应该在这种情况下使用关系。 如果不是,将事务记录到日志中的最佳做法是什么?

【问题讨论】:

  • 能否请您展示您的实体类型配置/类引用?
  • 每个表中的列 InvoiceId、CreditNoteId、DebitNoteId 链接到 DocumentId,其中强制外键约束设置为 false。类引用是如上所示的脚手架实体。
  • 我通过添加 OnModelCreating 编辑了问题
  • 看起来你想要按层次结构继承表。每个 FK 都需要一个单独的列,鉴别器 DocumentTypeId 表示正在使用的列。这违反了第三范式,顺便说一句。

标签: c# sql-server entity-framework


【解决方案1】:

在您的DbContext 中添加以下配置,然后添加迁移并相应地更新数据库。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Invoice>().HasMany(i => i.TransactionLog).WithOne(tl => tl.Invoice).HasForeignKey(tl => tl.DocumentId);
    modelBuilder.Entity<DebitNote>().HasMany(dn => dn.TransactionLog).WithOne(tl => tl.DebitNote).HasForeignKey(tl => tl.DocumentId);
    modelBuilder.Entity<CreditNote>().HasMany(cn => cn.TransactionLog).WithOne(tl => tl.CreditNote).HasForeignKey(tl => tl.DocumentId);
}

【讨论】:

    【解决方案2】:

    我认为也许外键的方向是错误的。

    作为您的表定义,TransactionLog.DocumentId 的值必须同时存在于三个表中(InvoiceCreditNoteDebitNote)。所以,如果只插入其中两个,可能会有异常。

    我想你真的希望表定义是这样的。 我删除了TransactionLog.DocumentId列,并将三个表的PK中的FK添加到TransactionLog.TransactionLogId

    实体:

    public partial class TransactionLog
    {
        public int TransactionLogId { get; set; }
        public Nullable<int> DocumentTypeId { get; set; }
        public Nullable<decimal> Amount { get; set; }
    
        public virtual CreditNote CreditNote { get; set; }
        public virtual DebitNote DebitNote { get; set; }
        public virtual Invoice Invoice { get; set; }
    }
    
    public partial class Invoice
    {
        public int InvoiceId { get; set; }
        public string InvoiceNumber { get; set; }
        public decimal Amount { get; set; }
    
        public virtual TransactionLog TransactionLog { get; set; }
    }
    
    public partial class CreditNote
    {
        public int CreditNoteId { get; set; }
        public string CreditNoteNumber { get; set; }
        public decimal Amount { get; set; }
    
        public virtual TransactionLog TransactionLog { get; set; }
    }
    
    public partial class DebitNote
    {
        public int DebitNoteId { get; set; }
        public string DebitNoteNumber { get; set; }
        public decimal Amount { get; set; }
    
        public virtual TransactionLog TransactionLog { get; set; }
    }
    

    代码:

    Invoice invoice = new Invoice() { InvoiceNumber = "Inv0100", Amount = 66m };
    TransactionLog invoiceLog = new TransactionLog() { Amount = invoice.Amount, DocumentTypeId = 1 };
    invoice.TransactionLog = invoiceLog;
    _context.Invoices.Add(invoice);
    
    CreditNote creditNote = new CreditNote() { CreditNoteNumber = "DN003", Amount = 99.99m };
    TransactionLog creditNoteLog = new TransactionLog() { Amount = creditNote.Amount, DocumentTypeId = 2 };
    creditNote.TransactionLog = creditNoteLog;
    _context.CreditNotes.Add(creditNote);
    
    DebitNote debitNote = new DebitNote() { DebitNoteNumber = "CN009", Amount = 77.77m };
    TransactionLog debitNoteLog = new TransactionLog() { Amount = debitNote.Amount, DocumentTypeId = 3 };
    debitNote.TransactionLog = debitNoteLog;
    _context.DebitNotes.Add(debitNote);
    

    【讨论】:

    • 一张Invoice可能有多个TransactionLog。例如创建和取消发票,记录到 TransactionLog。
    • @billy_flow 抱歉,我错过了将强制外键约束设置为false。我设置了它并再次尝试了您的代码。 DocumentId 可以正确设置。也许您问题中DocumentId 的值是正确的。
    • 请尝试将 InvoiceId、CreditNoteId 和 DebitNoteId 设置为不同的值。所以你可以在 DocumentId 中看到不同。
    • 是的。我将 InvoiceId、CreditNoteId 和 DebitNoteId 设置为不同的值,DocumentId 也不同。对吗?
    【解决方案3】:

    我猜你的关系有问题。就像一笔交易可以有多张发票,但一张发票只有一个交易记录。我可能错了,因为有时感觉像是一对一的,但无论如何我试了一下,这就是你想要的。

    实体:

    public class TestMVCEntities : DbContext
    {
    
        public TestMVCEntities()
            : base("name=TestMVCEntities")
        {
        }
    
        public DbSet<Invoice> Invoices { get; set; }
        public DbSet<DebitNote> DebitNotes { get; set; }
        public DbSet<CreditNote> CreditNotes { get; set; }
        public DbSet<TransactionLog> TransactionLogs { get; set; }
    
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<TransactionLog>()
                .HasRequired(p => p.Invoice)
                .WithMany(p => p.InvoiceLog)
                .HasForeignKey(p => p.DocumentId);
    
            modelBuilder.Entity<TransactionLog>()
                .HasRequired(p => p.DebitNote)
                .WithMany(p => p.DebitLog)
                .HasForeignKey(p => p.DocumentId);
    
            modelBuilder.Entity<TransactionLog>()
                .HasRequired(p => p.CreditNote)
                .WithMany(p => p.CreditLog)
                .HasForeignKey(p => p.DocumentId);
        }
    }
    
    public partial class TransactionLog
    {
        public int TransactionLogId { get; set; }
        public int? DocumentId { get; set; }
        public int? DocumentTypeId { get; set; }
        public decimal? Amount { get; set; }
    
        public CreditNote CreditNote { get; set; }
        public Invoice Invoice { get; set; }
        public DebitNote DebitNote { get; set; }
    }
    
    public partial class Invoice
    {
        public int InvoiceId { get; set; }
        public string InvoiceNumber { get; set; }
        public decimal Amount { get; set; }
    
        public ICollection<TransactionLog> InvoiceLog { get; set; }
    }
    
    public partial class DebitNote
    {
        public int DebitNoteId { get; set; }
        public string DebitNoteNumber { get; set; }
        public decimal Amount { get; set; }
    
        public ICollection<TransactionLog> DebitLog { get; set; }
    }
    
    public partial class CreditNote
    {
        public int CreditNoteId { get; set; }
        public string CreditNoteNumber { get; set; }
        public decimal Amount { get; set; }
    
        public ICollection<TransactionLog> CreditLog { get; set; }
    }  
    

    并插入数据:

    var invoice = new Invoice()
        {
            InvoiceNumber = "Inv099",
            Amount = 66m,
            InvoiceLog = new Collection<TransactionLog>()
            {
                new TransactionLog(){DocumentTypeId = 1, Amount = 66m}
            }
        };
    
        var creditNote = new CreditNote()
        {
            CreditNoteNumber = "DN002",
            Amount = 99.99m,
            CreditLog = new Collection<TransactionLog>()
            {
                new TransactionLog(){DocumentTypeId = 3, Amount = 99.99m}
            }
        };
    
        var debitNote = new DebitNote()
        {
            DebitNoteNumber = "CN008",
            Amount = 77.77m,
            DebitLog = new Collection<TransactionLog>()
            {
                new TransactionLog(){DocumentTypeId = 2, Amount = 77.77m}
            }
        };
    
        using (var context = new TestMVCEntities())
        {
            context.Invoices.Add(invoice);
            context.CreditNotes.Add(creditNote);
            context.DebitNotes.Add(debitNote);
            context.SaveChanges();
        }  
    

    表格将如下所示:

    【讨论】:

    • 请尝试将 InvoiceId、CreditNoteId 和 DebitNoteId 设置为不同的值。所以你可以在 DocumentId 中看到不同。
    猜你喜欢
    • 1970-01-01
    • 2018-09-24
    • 2021-09-03
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多