EF Core 3.1.7使用数据库优先和复合键关系会导致无效的SQL

问题描述

我在sql InvoiceHeaderInvoiceLines中有两个表,它们具有一对多的关系,其中四列用作外键。我很容易从数据库生成实体对象,并且可以查询InvoiceHeader对象,而不会出现问题。

当我在查询包括InvoiceLines或尝试单独查询它们时,EF生成的结果sql在select语句中包括InvoiceHeader表名和每个键列作为fieldName(示例InvoiceHeader.StoreID作为InvoiceHeaderStoreID包含在select语句中)。

脚手架生成的对象和上下文构建:

InvoiceHeader

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BackOfficesql.Data.Models
{
    public partial class InvoiceHeader
    {
        public InvoiceHeader()
        {
            InvoiceLines = new HashSet<InvoiceLines>();
        }

        [Key]
        [Column("StoreID")]
        public short StoreId { get; set; }
        [Key]
        [Column("CashRegisterID")]
        public short CashRegisterId { get; set; }
        [Key]
        [Column("ShiftCounterID")]
        public short ShiftCounterId { get; set; }
        [Key]
        [Column("InvoiceID")]
        public short InvoiceId { get; set; }
        [Column("TransactionTypeID")]
        public short TransactionTypeId { get; set; }
        [Column("CashierID")]
        public short? CashierId { get; set; }
        [Column(TypeName = "datetime")]
        public DateTime? SetupDate { get; set; }
        public bool? Audited { get; set; }
        [StringLength(50)]
        public string AuditedBy { get; set; }
        [Column("vendorID")]
        public int? vendorId { get; set; }
        [Column("POHeadID")]
        public int? PoheadId { get; set; }
        [StringLength(50)]
        public string vendorInvoice { get; set; }
        [Column(TypeName = "datetime")]
        public DateTime? InvoiceDate { get; set; }
        [Column("upsize_ts")]
        public byte[] UpsizeTs { get; set; }

        [ForeignKey(nameof(TransactionTypeId))]
        [InverseProperty(nameof(TransType.InvoiceHeader))]
        public virtual TransType TransactionType { get; set; }
        [InverseProperty("InvoiceHeader")]
        public virtual InvoiceTruckInfo InvoiceTruckInfo { get; set; }
        [InverseProperty("InvoiceHeader")]
        public virtual ICollection<InvoiceLines> InvoiceLines { get; set; }
    }
}

发票行

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BackOfficesql.Data.Models
{
    public partial class InvoiceLines
    {
        [Key]
        [Column("StoreID")]
        public short StoreId { get; set; }
        [Key]
        [Column("CashRegisterID")]
        public short CashRegisterId { get; set; }
        [Key]
        [Column("ShiftCounterID")]
        public short ShiftCounterId { get; set; }
        [Key]
        [Column("InvoiceID")]
        public short InvoiceId { get; set; }
        [Key]
        [Column("InvoiceLineID")]
        public short InvoiceLineId { get; set; }
        [Key]
        [Column("DepartmentTypeID")]
        public short DepartmentTypeId { get; set; }
        [Key]
        public int ItemId { get; set; }
        [Column("DepartmentID")]
        public short? DepartmentId { get; set; }
        [Column("MeterID")]
        public short? MeterId { get; set; }
        [StringLength(100)]
        public string Description { get; set; }
        public double Quantity { get; set; }
        [Column(TypeName = "money")]
        public decimal ExtendedPrice { get; set; }
        public bool? Taxable { get; set; }
        public double? TaxRate { get; set; }
        public short? PriceLevel { get; set; }
        public double? AfterQuantity { get; set; }
        [Column(TypeName = "money")]
        public decimal? UnitCost { get; set; }
        [Column("UPCCode")]
        [StringLength(15)]
        public string Upccode { get; set; }
        public short? UpdateOnhand { get; set; }
        [Column("TableID")]
        public int? TableId { get; set; }
        [Column("upsize_ts")]
        public byte[] UpsizeTs { get; set; }

        [ForeignKey("ShiftCounterId,CashRegisterId,InvoiceId,StoreId")]
        [InverseProperty("InvoiceLines")]
        public virtual InvoiceHeader InvoiceHeader { get; set; }
    }
}

InvoiceHeader DBContext

public virtual DbSet<InvoiceHeader> InvoiceHeader { get; set; }

modelBuilder.Entity<InvoiceHeader>(entity =>
    {
        entity.HasKey(e => new { e.ShiftCounterId,e.CashRegisterId,e.InvoiceId,e.StoreId })
            .HasName("aaaaaInvoiceHeader_PK");

        entity.HasIndex(e => e.PoheadId)
            .HasName("POHeadID");

        entity.HasIndex(e => e.vendorId)
            .HasName("vendorID");

        entity.HasIndex(e => new { e.InvoiceDate,e.TransactionTypeId })
            .HasName("IX_InvoiceHeader_InvoiceDate");

        entity.HasIndex(e => new { e.ShiftCounterId,e.TransactionTypeId,e.StoreId })
            .HasName("TransactionTypeID")
            .IsUnique();

        entity.Property(e => e.ShiftCounterId).HasDefaultValuesql("(0)");
        entity.Property(e => e.CashRegisterId).HasDefaultValuesql("(0)");
        entity.Property(e => e.InvoiceId).HasDefaultValuesql("(0)");
        entity.Property(e => e.StoreId).HasDefaultValuesql("(0)");
        entity.Property(e => e.Audited).HasDefaultValuesql("(0)");
        entity.Property(e => e.AuditedBy).IsUnicode(false);
        entity.Property(e => e.CashierId).HasDefaultValuesql("(0)");
        entity.Property(e => e.InvoiceDate).HasDefaultValuesql("(getdate())");
        entity.Property(e => e.PoheadId).HasDefaultValuesql("(0)");
        entity.Property(e => e.SetupDate).HasDefaultValuesql("(getdate())");
        entity.Property(e => e.TransactionTypeId).HasDefaultValuesql("(1)");
        entity.Property(e => e.UpsizeTs)
            .IsRowVersion()
            .IsConcurrencyToken();

        entity.Property(e => e.vendorId).HasDefaultValuesql("(0)");
        entity.Property(e => e.vendorInvoice).IsUnicode(false);

        entity.HasOne(d => d.TransactionType)
            .WithMany(p => p.InvoiceHeader)
            .HasForeignKey(d => d.TransactionTypeId)
            .OnDelete(DeleteBehavior.ClientSetNull)
            .HasConstraintName("FK_InvoiceHeader_TransType");
    });

DbContext

    public virtual DbSet<InvoiceLines> InvoiceLines { get; set; }
    
    modelBuilder.Entity<InvoiceLines>(entity =>
                {
                    entity.HasKey(e => new { e.ShiftCounterId,e.InvoiceLineId,e.DepartmentTypeId,e.StoreId,e.ItemId });
    
                    entity.HasIndex(e => e.ItemId)
                        .HasName("ItemId");
    
                    entity.HasIndex(e => e.MeterId)
                        .HasName("IX_MeterID_ServerID");
    
                    entity.HasIndex(e => e.Upccode)
                        .HasName("UPCCode");
    
                    entity.HasIndex(e => e.UpdateOnhand)
                        .HasName("IX_UpdateOnhand");
    
                    entity.HasIndex(e => new { e.ShiftCounterId,e.StoreId })
                        .HasName("IX_InvoiceLines");
    
                    entity.HasIndex(e => new { e.StoreId,e.ShiftCounterId,e.InvoiceLineId })
                        .HasName("UK_InvoiceLines")
                        .IsUnique();
    
                    entity.Property(e => e.ShiftCounterId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.CashRegisterId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.InvoiceId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.InvoiceLineId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.DepartmentTypeId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.StoreId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.ItemId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.AfterQuantity).HasDefaultValuesql("((-1))");
                    entity.Property(e => e.DepartmentId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.Description).IsUnicode(false);
                    entity.Property(e => e.ExtendedPrice).HasDefaultValuesql("(0)");
                    entity.Property(e => e.MeterId).HasDefaultValuesql("(0)");
                    entity.Property(e => e.PriceLevel).HasDefaultValuesql("(0)");
                    entity.Property(e => e.Quantity).HasDefaultValuesql("(0)");
                    entity.Property(e => e.TaxRate).HasDefaultValuesql("(0)");
                    entity.Property(e => e.Taxable).HasDefaultValuesql("(0)");
                    entity.Property(e => e.UnitCost).HasDefaultValuesql("(0)");
                    entity.Property(e => e.Upccode).IsUnicode(false);
                    entity.Property(e => e.UpdateOnhand).HasDefaultValuesql("(0)");
                    entity.Property(e => e.UpsizeTs)
                        .IsRowVersion()
                        .IsConcurrencyToken();
    
                    entity.HasOne(d => d.InvoiceHeader)
                        .WithMany(p => p.InvoiceLines)
                        .HasForeignKey(d => new { d.ShiftCounterId,d.CashRegisterId,d.InvoiceId,d.StoreId })
                        .HasConstraintName("FK_InvoiceLines_InvoiceHeader");
                });

测试实体查询

Invoice = context.InvoiceHeader.Where(x => x.StoreId == 1 && x.CashRegisterId == 54 && x.ShiftCounterId == 1 && x.InvoiceId == 24)
                               // .Include(l => l.InvoiceLines) //Causes An exception
                               .Include(t => t.InvoiceTruckInfo)
                               .FirstOrDefault();

为找出问题所在,我能够查询InvoiceLines实体并使用反射方法来找到EF生成sql

var query = context.InvoiceLines.Where(x => x.StoreId == 1 && x.CashRegisterId == 54 && x.ShiftCounterId == 1 && x.InvoiceId == 24);

var sql = query.Tosql();

生成sql语句为:

SELECT [i].[ShiftCounterID],[i].[CashRegisterID],[i].[InvoiceID],[i].[InvoiceLineID],[i].[DepartmentTypeID],[i].[StoreID],[i].[ItemId],[i].[AfterQuantity],[i].[DepartmentID],[i].[Description],[i].[ExtendedPrice],[i].[InvoiceHeaderCashRegisterId],[i].[InvoiceHeaderInvoiceId],[i].[InvoiceHeaderShiftCounterId],[i].[InvoiceHeaderStoreId],[i].[MeterID],[i].[PriceLevel],[i].[Quantity],[i].[TableID],[i].[TaxRate],[i].[Taxable],[i].[UnitCost],[i].[UPCCode],[i].[UpdateOnhand],[i].[upsize_ts]
FROM [InvoiceLines] AS [i]
WHERE ((([i].[StoreID] = CAST(1 AS smallint)) 
AND ([i].[CashRegisterID] = CAST(54 AS smallint))) 
AND ([i].[ShiftCounterID] = CAST(1 AS smallint))) 
AND ([i].[InvoiceID] = CAST(24 AS smallint))

以下4列无效,如果我从上面的sql删除它们,则可以无问题地运行SQL查询

   [i].[InvoiceHeaderCashRegisterId],

我已经尝试过对EF实用程序生成的实体进行各种修改,即使我在示例和文档中所看到的一切都使实体和上下文构建看起来正确。我不确定将表名和字段名结合使用的原因或原因,将其插入到生成sql select语句中。

执行代码查询InvoiceHeader包括执行的InvoiceLinesInvoiceLines查询时,毫无疑问地会抛出sql异常:

无效的列名'InvoiceHeaderCashRegisterId'。
无效的列名'InvoiceHeaderInvoiceId'。
无效的列名'InvoiceHeaderShiftCounterId'。
无效的列名'InvoiceHeaderStoreId'。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...