【问题标题】:EF Core Update-Database failing when creating Foreign key创建外键时EF Core更新数据库失败
【发布时间】:2020-07-28 09:42:17
【问题描述】:

当我运行Update-Database 时出现以下错误:

fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
   Failed executing DbCommand (938ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
   ALTER TABLE [port_portal] ADD CONSTRAINT [FK_port_portal_port_portal_page_default] FOREIGN KEY ([default_page_id], [organization_id]) REFERENCES [port_portal_page] ([page_id], [organization_id]) ON DELETE NO ACTION;
Failed executing DbCommand (938ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER TABLE [port_portal] ADD CONSTRAINT [FK_port_portal_port_portal_page_default] FOREIGN KEY ([default_page_id], [organization_id]) REFERENCES [port_portal_page] ([page_id], [organization_id]) ON DELETE NO ACTION;
Microsoft.Data.SqlClient.SqlException (0x80131904): The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_port_portal_port_portal_page_default". The conflict occurred in database "ArenaSprint", table "dbo.port_portal_page".
   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.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean isAsync, Int32 timeout, Boolean asyncWrite)
   at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName)
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
ClientConnectionId:8a9a2e38-2f14-4185-818f-919fe99bc5fb
Error Number:547,State:0,Class:16
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_port_portal_port_portal_page_default". The conflict occurred in database "ArenaSprint", table "dbo.port_portal_page".

如果我运行相同的脚本,它会尝试直接在 SSMS 中的迁移中运行,它可以正常工作。那么为什么在这里失败呢?模型和上下文是从现有数据库进行逆向工程的。当我进入 SSMS 并为具有此外键的表生成 DROP AND CREATE 脚本时,它会为该外键提供不同的 SQL。

这是我的 DbContext 中 OnModelCreate() 中的部分

modelBuilder.Entity<PortPortal>(entity =>
    {
        entity.HasKey(e => e.PortalId)
            .HasName("PK_orgn_portal");

        entity.HasComment(@"Contains Portal inforamation dministration > Portal List");

        entity.HasIndex(e => e.DefaultPageId)
            .HasName("IX_port_portal");

        entity.Property(e => e.CreatedBy).IsUnicode(false);

        entity.Property(e => e.DateCreated).HasDefaultValueSql("(getdate())");

        entity.Property(e => e.DateModified).HasDefaultValueSql("(getdate())");

        entity.Property(e => e.DefaultDomain)
            .IsUnicode(false)
            .HasDefaultValueSql("('')")
            .HasComment(@"Field: Default Domain");

        entity.Property(e => e.DefaultPageId).HasComment(@"Field: Default Page ID");

        entity.Property(e => e.FormsAuthentication)
            .HasDefaultValueSql("((1))")
            .HasComment(@"Field: Authentication  ");

        entity.Property(e => e.JqueryInclude).IsUnicode(false);

        entity.Property(e => e.LoginPageId).HasComment(@"Field: Login Page ID");

        entity.Property(e => e.ModifiedBy).IsUnicode(false);

        entity.Property(e => e.NavigationStyleSheet).IsUnicode(false);

        entity.Property(e => e.PortalDesc)
            .IsUnicode(false)
            .HasComment(@"Field: Portal Description");

        entity.Property(e => e.PortalName)
            .IsUnicode(false)
            .HasComment(@"Field: Portal Name");

        entity.Property(e => e.PortalNotes)
            .IsUnicode(false)
            .HasComment(@"Field: Portal Notes");

        entity.Property(e => e.PortalStyleSheet)
            .IsUnicode(false)
            .HasDefaultValueSql("('')")
            .HasComment(@"Field: Portal Style Sheet");

        entity.Property(e => e.PortalTitle)
            .IsUnicode(false)
            .HasDefaultValueSql("('')")
            .HasComment(@"Field: Portal Title");

        entity.Property(e => e.TreeStyleSheet).IsUnicode(false);

        entity.HasOne(d => d.Organization)
            .WithMany(p => p.PortPortal)
            .HasForeignKey(d => d.OrganizationId)
            .OnDelete(DeleteBehavior.ClientSetNull)
            .HasConstraintName("FK_port_portal_orgn_organization");

        entity.HasOne(d => d.PortPortalPage)
            .WithMany(p => p.PortPortalPortPortalPage)
            .HasForeignKey(d => new { d.DefaultPageId, d.OrganizationId })
            .OnDelete(DeleteBehavior.ClientSetNull)
            .HasConstraintName("FK_port_portal_port_portal_page_default");

        entity.HasOne(d => d.PortPortalPageNavigation)
            .WithMany(p => p.PortPortalPortPortalPageNavigation)
            .HasForeignKey(d => new { d.LoginPageId, d.OrganizationId })
            .HasConstraintName("FK_port_portal_port_portal_page_login");

        entity.HasOne(d => d.PortPortalPage1)
            .WithMany(p => p.PortPortalPortPortalPage1)
            .HasForeignKey(d => new { d.PageNotFoundId, d.OrganizationId })
            .HasConstraintName("FK_port_portal_port_portal_page_not_found");

        entity.HasOne(d => d.PortPortalPage2)
            .WithMany(p => p.PortPortalPortPortalPage2)
            .HasForeignKey(d => new { d.PersonPopupPageId, d.OrganizationId })
            .HasConstraintName("FK_port_portal_port_portal_page_popup");

        entity.HasData(new PortPortal
            {
                PortalId = 1,
                CreatedBy = "Setup",
                ModifiedBy = "Setup",
                OrganizationId = 1,
                PortalName = "Staff Web",
                PortalTitle = "My Church Staff Portal",
                PortalDesc = "",
                PortalNotes = "",
                PortalDefLastUpdated = null,
                PortalStyleSheet = "",
                LoginPageId = null,
                DefaultDomain = "",
                FormsAuthentication = true,
                IsMobile = false,
                PageNotFoundId = null,
                PersonPopupPageId = null,
                TreeStyleSheet = null,
                NavigationStyleSheet = null,
                JqueryInclude = null,
                PortalEulaPrompt = true,
                BootstrapCssInclude = null,
                BootstrapJsInclude = null
            });
    });

以及迁移构建器代码:

migrationBuilder.AddForeignKey(
    name: "FK_port_portal_port_portal_page_default",
    table: "port_portal",
    columns: new[] { "default_page_id", "organization_id" },
    principalTable: "port_portal_page",
    principalColumns: new[] { "page_id", "organization_id" },
    onDelete: ReferentialAction.Restrict);

更新:我更新了具有OnModelCreating() 的代码块。这是播种数据的错误位置吗?

【问题讨论】:

    标签: entity-framework-core


    【解决方案1】:

    port_portal 表中的至少一个现有行可能已包含两个外键列(default_page_idorganization_id)之一中的值,该值没有对应的 ID主表 (port_portal_page)。

    这将违反数据库的引用完整性,因此ALTER TABLE 语句失败。

    运行以下 SQL 查询以查找无效引用:

    select *
    from port_portal as pp
    left join port_portal_page as ppp on ppp.default_page_id = pp.page_id AND ppp.organization_id = pp.organization_id
    where ppp.default_page_id is null;
    

    更新:我更新了具有 OnModelCreating() 的代码块。这是播种数据的错误位置吗?

    不,这个地方通常是正确的。看看Data Seeding,尤其是Limitations of model seed data 部分。

    您在哪里将迁移应用到数据库?在应用程序外部手动作为脚本(推荐)或作为应用程序代码的一部分(不鼓励)?

    如果您播种的只是一个PortPortal 实体,那么问题应该仍然是我上面描述的那个,因为您缺少PortPortalPage 实体的相应条目,并且引用完整性将再次被破坏。

    您可以通过执行Sql() 调用作为Up() 方法的一部分来测试这一点稍后),甚至在它之前调用 Debugger.Launch() 以触发 JIT 调试器,然后单步执行代码。

    【讨论】:

    • 我已经可以告诉你该语句将返回 0 行。我正在用新信息更新我的问题,但我认为这是因为我在 OnModelCreating() 中播种数据?我之前没有包括那部分。
    • port_portal 中只有一个条目,port_portal_page 中只有一个条目(如果Update-Database 命令成功完成)。
    • 我熟悉执行迁移的应用程序,但我目前没有这样做。我通过包管理器控制台手动运行迁移。除了使用 EF Core 之外,是否建议以其他方式播种数据?我可以编写脚本来处理这个问题。
    • 如果数据应该是迁移的一部分,那通常是可以的(但请参阅我引用的限制部分),但可能需要一些手动调整(请参阅Custom initialization logic)。检查操作顺序和跟踪问题的一种简单方法是使用 dotnet ef migrations script 生成迁移脚本并在 SSMS 中手动运行它,逐步执行每一行。
    • ALTER TABLE 语句运行之前,数据的引用完整性需要正确,使用生成的脚本应该不难验证。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    • 2021-05-25
    • 1970-01-01
    • 2021-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多