为什么更新外键后引用约束会不一致?

2024-06-29

抱歉,这个模糊的标题很难用一句话来描述:

我有 2 个实体User and UserAddress,其中 User 有 2 个外键DefaultInvoiceAddressId and DefaultDeliveryAddressId和 UserAddress 有一个UserId外键。

用户对象具有默认地址的导航属性(DefaultInvoiceAddress and DefaultDeliveryAddress)以及他所有地址的一个:AllAddresses.

映射等有效,创建和更新用户和地址也有效。

但不起作用的是将用户的现有地址设置为例如默认发票地址。用 SQL 术语来说,我想要发生的是UPDATE USER SET DefaultInvoiceAddressId = 5 WHERE Id = 3.

我已经尝试过以下方式:

private void MarkAs(User user, UserAddress address, User.AddressType type) {
        if (context.Entry(user).State == EntityState.Detached)
            context.Users.Attach(user);

        // guess I don't really need this:
        if (context.Entry(address).State == EntityState.Detached)
            context.UserAddresses.Attach(address);

        if (type.HasFlag(User.AddressType.DefaultInvoice)) {
            user.DefaultInvoiceAddressId = address.Id;
            user.DefaultInvoiceAddress = null;
            context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
        }

        if (type.HasFlag(User.AddressType.DefaultDelivery)) {
            user.DefaultDeliveryAddressId = address.Id;
            user.DefaultDeliveryAddress = null;
            context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
        }
    }

创建新用户地址以及更新地址时都会调用此方法。创建场景按预期工作,但是在更新情况下我收到以下错误:

The changes to the database were committed successfully, 
but an error occurred while updating the object context. 
The ObjectContext might be in an inconsistent state. 
Inner exception message: A referential integrity constraint violation occurred: 
The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.

我使用从数据库检索的 User 对象及其包含的默认传递地址来调用该方法,我通过预先加载将其与其一起加载。

var user = mainDb.User.Get(UnitTestData.Users.Martin.Id, User.Include.DefaultAddresses);
var existingAddress = user.DefaultDeliveryAddress;
mainDb.User.Addresses.SetAs(user, existingAddress, User.AddressType.DefaultInvoice))
// the SetAs method verfies input parameters, calls MarkAs and then SaveChanges

简而言之,我只想将用户的 DefaultDeliveryAddress 也设为他的 DefaultInvoiceAddress,这可以使用上面的 SQL Update 命令轻松完成,但我的 EF 代码遗漏了一些内容。 我已经检查过:

  • 仅设置了Id,导航属性(DefaultInvoiceAddress) 被重置为 null
  • UserAddress.UserId = User.Id(显然因为它已经分配给用户)
  • 用户对象将变成Modified(用调试器检查),因为它的属性之一被标记为已修改
  • 我还尝试清除两个默认地址导航属性,但这也没有帮助

我怀疑这个问题是由于 User 实体有 2 个对 UserAddress 的引用,并且两个外键都设置为引用同一个地址 - 我怎样才能让 EF 处理它?

Update:

以下是 User 实体的映射:

// from UserMap.cs:
...
        Property(t => t.DefaultInvoiceAddressId).HasColumnName("DefaultInvoiceAddressId");
        Property(t => t.DefaultDeliveryAddressId).HasColumnName("DefaultDeliveryAddressId");

        // Relationships
        HasOptional(t => t.DefaultInvoiceAddress)
            .WithMany()
            .HasForeignKey(t => t.DefaultInvoiceAddressId);

        HasOptional(t => t.DefaultDeliveryAddress)
            .WithMany()
            .HasForeignKey(t => t.DefaultDeliveryAddressId);

        HasMany(t => t.AllAddresses)
            .WithRequired()
            .HasForeignKey(t => t.UserId)
            .WillCascadeOnDelete();

UserAddress 没有返回 User 的导航属性;它只包含 HasMaxLength 和 HasColumnName 设置(我排除它们以保持问题的可读性)。

Update 2

这是 Intellitrace 执行的命令:

The command text "update [TestSchema].[User]
set [DefaultInvoiceAddressId] = @0
where ([Id] = @1)
" was executed on connection "Server=(localdb)\..."

我觉得不错;似乎只有 EF 状态管理器会对键映射感到困惑。


解决了问题:显然,何时将导航属性设置为 null 会产生很大的差异,因为 EF 可能会将其解释为预期的更改/更新(至少我是这么怀疑的)。

以下版本的MarkAs方法的工作原理:

private void MarkAs(User user, UserAddress address, User.AddressType type) {
        if (context.Entry(user).State == EntityState.Detached) {
            // clear navigation properties before attaching the entity
            user.DefaultInvoiceAddress = null;
            user.DefaultDeliveryAddress = null;
            context.Users.Attach(user);
        }
        // address doesn't have to be attached

        if (type.HasFlag(User.AddressType.DefaultInvoice)) {
            // previously I tried to clear the navigation property here
            user.DefaultInvoiceAddressId = address.Id;
            context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
        }

        if (type.HasFlag(User.AddressType.DefaultDelivery)) {
            user.DefaultDeliveryAddressId = address.Id;
            context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
        }
    }

为未来的读者总结我的发现:

  • 如果您打算通过外键属性更新实体,请清除导航属性。 EF 不需要它们来找出更新语句。
  • 清除导航属性before您将一个实体附加到上下文,否则 EF 可能会将其解释为更改(在我的情况下,外键可为空,如果不是这种情况,EF 可能足够聪明,可以忽略导航属性更改)。

我不会立即接受我自己的答案,以便给其他(更有资格的)读者提供回答的机会;如果在接下来的 2 天内没有发布答案,我将接受此答案。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么更新外键后引用约束会不一致? 的相关文章

  • 以同样的方式根据编号对数组进行洗牌

    我正在运行一个测验制作网站 我希望以打乱的顺序向用户显示问题的答案 我试图avoid如果我要随机打乱答案 则存储向用户呈现答案的顺序 我想以可预测的方式打乱答案 这样我以后就可以用同样的方式重复随机播放 显示结果时 我认为我可以按某个数字对
  • 获取 ES6 符号的描述

    我想知道是否有一种很好的方法来获取符号的描述 例如 var s Symbol 5 toString 的默认实现将简单地打印 符号 5 我想找到一种方法来提取实际描述 即 5 Symbol description https develope
  • Android:getIntent() 已弃用

    我的程序由一个 MainActivity 和两个片段活动组成 我需要一个片段从用户那里获取一个字符串值并将其传递给第二个片段 我正在努力思考如何做到这一点 由于我熟悉意图 我发现这个答案 https stackoverflow com qu
  • 配置 SourceTrail 以接受带有 @ 语法的嵌入式 c/c++ 头文件

    我正在尝试使用 Sourcetrail https www sourcetrail com https www sourcetrail com 快速了解 pic18 系列微控制器的一些旧嵌入式 c c 源代码 导入硬件包含文件时出现错误 该
  • 在 PowerShell 中更改错误消息语言

    我正在尝试在 powershell 中以英文显示错误 以便我可以更轻松地在线搜索它们 当出现错误时 它会以法语显示 如下所示 PS C Users Olivier lpthw gt type nul gt ex2 py type Impos
  • 使用具有阿拉伯字符的 json.dumps 将字典转换为 json [重复]

    这个问题在这里已经有答案了 我有一本包含阿拉伯语单词的字典 例如 data name name print json dumps data file open data json a encoding utf 8 Output name u
  • 在Python中使用argparse解析整个JSON

    我正在尝试使用 ARGPARSE 库在一个简单的参数中解析整个 Json 问题是当它遇到儿子内部的不同元素 例如 和 时 它会突然停止 这是测试代码 parse py import argparse parser argparse Argu
  • 从 Google 地图中的纬度、经度搜索生成英国邮政编码

    我正在尝试通过 Google 地图中的纬度和经度搜索生成英国邮政编码 例如 在 Google 地图中搜索 57 350237 1 977539 将返回以下内容 https i stack imgur com mSULM png https
  • 如何使用元类中的方法更改 groovy 中方法的行为

    我想通过以下方式 破坏 Groovy 中的 plus 方法 Integer metaClass plus Integer n gt delegate n 1 assert 2 2 5 我收到 StackOverflowException 这
  • HTTPS文件下载C#

    我需要下载隐藏在 HTTPS 连接后面的文件 我对下载安全网站后面的文件不熟悉 我尝试使用凭据来解决此问题 但无济于事 据我所知 您需要创建并使用证书来完成此操作 但我没有找到任何示例 任何帮助表示赞赏 这就是我现在所拥有的 WebClie
  • FullCalendar 日期之间的分割线

    我正在使用 jquery fullcalendar 它工作得很好 但是在议程周视图中 日期之间没有分界线 我查看了 fullcalendar 网站 甚至试图找到 css 处理显示分界线的方式 但没有成功 如何显示日期分割线 Thanks 我
  • Oracle 难以管理简单任务的说法正确吗?没有优质的管理应用程序吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我在这里看到这个说法是在咆哮中提出的http discuss joelonsoftware com default asp joel 3 456
  • 有人可以推荐 java 8 模式来替换 switch 语句吗?

    我有以下代码 public class A private String type String getType return type 现在在许多代码位置我都有这样的代码 switch a geType case A return new
  • 将静态站点生成器与 php 集成

    我目前正在使用 php 构建一个不需要定期更新的网站 并且我考虑使用静态站点生成器 因为它将具有类似博客的功能 然而 我的网站包含一个将与数据库链接的表单 我遇到的问题是静态站点生成器无法识别和解析 php 我目前正在考虑使用三个静态站点生
  • 带骨干的递归函数jquery

    我在主干中有一个应用程序 我想在 Json 中找到一些记录并打印出来 我的 JSON 是这样的 id r1 hotel id 1 name Single level 1 id r1 1 hotel id 1 name Double leve
  • 正确使用 GuzzleHttp/Psr7/Response

    不确定在 php 页面中显示 Psr7 Guzzle Response 的正确方法是什么 现在 我正在做 use GuzzleHttp Psr7 BufferStream use GuzzleHttp Psr7 Response class
  • 使用数据绑定后查看边距不起作用

    下面是gridview的item布局的代码 在此 layout margin 位于设计面板中 但当我运行时没有边距 我尝试检查其他标签 例如背景 发现它有效 类似的问题是线性布局的布局权重 它们也不起作用 这些在没有数据绑定的情况下工作得很
  • 从 pexpect 中提取 stderr

    我的问题很简单 我可以吗 expect 使用 pexpect 查看 stderr 上的某些输出 它似乎pexpect spawn 只能用于期望 stdout 上的输出 乌托邦的例子 import pexpect child pexpect
  • Python 内存使用情况

    因此 我有一些代码接收一组文件 将其可以缝合在一起 然后绘制它们 我发布了大部分代码 试图使其更具可读性 如果需要 可以添加更多代码 for paths dirs files in os walk start path for d in d
  • 更新 Android Studio 后 Android 模拟器无法运行

    我通常使用 Android Studio 从 2 2 3 更新到 2 3 后 我的模拟器不再工作 这也很困难 因为 Google 决定弃用独立 SDK 管理器 我发现我可以检查 选择 显示包详细信息 但我仍然无法让我的 Google And

随机推荐