Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/280.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么更新外键后引用约束变得不一致?_C#_Entity Framework_Sql Update_Entity Framework 5_Foreign Key Relationship - Fatal编程技术网

C# 为什么更新外键后引用约束变得不一致?

C# 为什么更新外键后引用约束变得不一致?,c#,entity-framework,sql-update,entity-framework-5,foreign-key-relationship,C#,Entity Framework,Sql Update,Entity Framework 5,Foreign Key Relationship,抱歉,标题含糊不清,很难用一行字来描述: 我有两个实体User和UserAddress,其中User有两个外键DefaultInvoiceAddressId和DefaultDeliveryAddressId,UserAddress有一个UserId外键 用户对象具有默认地址(DefaultInvoiceAddress和DefaultDeliveryAddress)的导航属性,以及其所有地址的导航属性:AllAddresses 映射等工作,创建和更新用户和地址也工作 但不起作用的是将用户的现有地址

抱歉,标题含糊不清,很难用一行字来描述:

我有两个实体
User
UserAddress
,其中User有两个外键
DefaultInvoiceAddressId
DefaultDeliveryAddressId
,UserAddress有一个
UserId
外键

用户对象具有默认地址(
DefaultInvoiceAddress
DefaultDeliveryAddress
)的导航属性,以及其所有地址的导航属性:
AllAddresses

映射等工作,创建和更新用户和地址也工作

但不起作用的是将用户的现有地址设置为DefaultInvoiceAddress。在SQL术语中,我希望发生的是
更新用户集DefaultInvoiceAddressId=5,其中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.
我使用从数据库中检索到的用户对象和它包含的DefaultDeliveryAddress调用该方法,我通过即时加载将其加载到该方法旁边

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(显然,因为它已经分配给用户)
  • 由于用户对象的一个属性被标记为已修改,因此该用户对象将变为已修改(通过调试器检查)
  • 我还尝试清除两个默认地址导航属性,但也没有帮助
我怀疑这个问题是由于用户实体有2个对UserAddress的引用,并且两个外键都被设置为引用同一个地址-我如何让EF使用它

更新:

以下是用户实体的映射:

// 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没有返回给用户的导航属性;它只包含HasMaxLength和HasColumnName设置(我排除了它们以保持问题的可读性)

更新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不需要他们找出update语句
  • 在将实体附加到上下文之前,请清除导航属性,否则EF可能会将其解释为更改(在我的情况下,外键可以为空,如果不是这样,EF可能足够聪明,可以忽略导航属性更改)

我不会马上接受我自己的答案,给其他(更有资格的)读者一个回答的机会;如果在接下来的两天内没有回复,我将接受此回复。

发布用户和地址定义和配置。同时发布实际生成的SQL语句。嗯,SELECT可以简单地从查询中获取,但对于更新/插入,您需要SQL Mgmt studio或配置文件(迷你配置文件器似乎值得一看)。我找到了一种使用Intellitrace获取语句的方法,我已经用命令更新了我的问题。