EF Core,从 netcore2.2 更新到 netcore3.1 后出现无效列名异常

EF Core, Invalid column name exception after update from netcore2.2 to netcore3.1

自从我从 .netcore2.2 更新到 .netcore3.1 后,我遇到了新的奇怪异常 "Invalid column name 'TenantTemplateTypeID1'"。请不要是“1”,它不应该出现在这里。代码中的任何地方都没有 TenantTemplateTypeID1,所以我假设它是由 EF 核心生成的。

EF核心版本:3.1.1

异常:

Exception: Invalid column name 'TenantTemplateTypeID1'.
Invalid column name 'TenantTemplateTypeID1'.
Invalid column name 'TenantTemplateTypeID1'.
Invalid column name 'TenantTemplateTypeID1'.
StactTrace:    at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
   at Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()

查询:

var t = engineCtx.TenantTemplates
                 .AsNoTracking()
                 .Include(tt => tt.TenantTemplateParameters)
                 .Include(tt => tt.TenantTemplateStyles)
                 .FirstOrDefault(tt => tt.TenantTemplateTypeID == tenantTemplateTypeID);

DbContext

modelBuilder.Entity<Db.Model.TenantTemplate>(entity =>
            {
                entity.HasKey(e => e.TenantTemplateTypeID)
                      .HasName("PK_TenantTemplate_TenantTemplateTypeID");

                entity.ToTable("TenantTemplates", "templateEngine");

                entity.Property(e => e.TenantTemplateTypeID)
                      .HasColumnName("TenantTemplateTypeId");
                //... not related rows removed
                entity.HasMany(e => e.TenantTemplateParameters)
                      .WithOne(e => e.TenantTemplate)
                      .HasConstraintName("FK_TenantTemplate_TenantTemplateParameters")
                      .OnDelete(DeleteBehavior.Restrict);

                entity.HasMany(e => e.TenantTemplateStyles)
                      .WithOne(e => e.TenantTemplate)
                      .HasConstraintName("FK_TenantTemplate_TenantTemplateStyles")
                      .OnDelete(DeleteBehavior.Restrict);
            });

DB 模型只包含没有任何属性的属性。

public partial class TenantTemplate
    {
         public long TenantTemplateTypeID { get; set; }
         // not related rows removed
         public virtual ICollection<TenantTemplateParameter> TenantTemplateParameters { get; set; }
         public virtual ICollection<TenantTemplateStyle> TenantTemplateStyles { get; set; }
    }
// TenantTemplateStyle is exacly the same (just PK has a different name)
public class TenantTemplateParameter
   {
        public long TenantTemplateParameterID { get; set; }
        public long TenantTemplateTypeID { get; set; }
        // rows removed
        public virtual TenantTemplate TenantTemplate { get; set; }
   }

有人知道一些overround吗?谢谢...

我很确定这是由以下 EF Core 3.0 重大更改引起的 - The foreign key property convention no longer matches same name as the principal property

根据该规则,TenantTemplateParameter 中的 TenantTemplateTypeID 不被视为 FK 名称。

奇怪的是,同时根据Foreign key shadow properties中解释的规则,它是默认的约定FK名称:

The property will be named <navigation property name><principal key property name> (the navigation on the dependent entity, which points to the principal entity, is used for the naming). If the principal key property name includes the name of the navigation property, then the name will just be <principal key property name>. If there is no navigation property on the dependent entity, then the principal type name is used in its place.

由于名称已被 "non FK" 属性 保留,常规名称生成器将索引添加到默认常规名称,因此观察到的行为。

我认为这是当前 EF Core 实现的 bug/defect。 workaround/solution 当然是显式映射 FK 属性,例如

entity.HasMany(e => e.TenantTemplateParameters)
      .WithOne(e => e.TenantTemplate)
      .HasForeignKey(e => e.TenantTemplateTypeID) // <--
      .HasConstraintName("FK_TenantTemplate_TenantTemplateParameters")
      .OnDelete(DeleteBehavior.Restrict);

如果其他关系(TenantTemplateStyle)使用相同的命名约定,则类似。