无法更改关系,因为一个或多个外键属性不可为空 [英] The relationship could not be changed because one or more of the foreign-key properties is non-nullable

查看:111
本文介绍了无法更改关系,因为一个或多个外键属性不可为空的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(注意:重复

(NOTE: This is not a duplicate of this question even though it has the same exception.)

我有一个


  1. 插入父记录和子记录

  2. 执行长时间运行的操作。

  3. 如果长时间运行的操作失败,请删除先前插入的父和子记录。

当我尝试步骤3时,我收到以下消息:

When I attempt step 3, I get the following message:


操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,将相关的外键属性设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,否则必须删除不相关的对象。

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

我一般来说这是什么意思,但是我以为我是按照规则玩的,不管我按照规则玩多么努力,我不确定为什么我

I understand generally what this means, but I thought I was playing by the rules and no matter how hard I try to play by the rules, I'm unsure why I'm getting this message.

我们使用自我跟踪实体,我的代码实际上是这样的:

We use self-tracking entities and my code is effectively this:

var parent = new Parent(1,2,3);
var child = new Child(4,5,6);
parent.Children.Add(child);

MyContext.Parents.ApplyChanges(parent);
MyContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);

// At this point, inserts were successful and entities are in an Unchanged state.
// Also at this point, I see that parent.Children.Count == 1

var shouldDeleteEntities = false;
try
{
  // This is not database-related. This process does some
  // encryption/decryption and uploads some files up to
  // Azure blob storage. It doesn't touch the DB.
  SomeLongRunningProcess();
}
catch
{
  // Oops, something bad happened. Let's delete the entities!
  shouldDeleteEntities = true;
}

// At this point, both entities are in an Unchanged state, child still
// appears in parent.Children, nothing is wrong that I can see.
parent.MarkAsDeleted();
child.MarkAsDeleted();

// I've tried MyContext.ApplyChanges here for both entities, no change.

// At this point, everything appears to be in the state that
// they're supposed to be!
try
{
  MyContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
}
catch
{
  // This exception was thrown and I can't figure out why!
}

这个逻辑有什么问题?为什么我不能简单地删除这两个记录?我调用 MarkAsDeleted 后,尝试调用 MyContext.ApplyChanges 。我尝试过各种各样的事情,无论什么,无论我尝试告诉上下文,我想要两者都被删除,它不断抛出这个异常。

What's wrong with this logic? Why am I not able to simply delete these two records? I've tried calling MyContext.ApplyChanges after I call MarkAsDeleted. I've tried all sorts of things and no matter what, no matter how hard I try to tell the Context that I want both of them deleted, it keeps throwing this exception.

推荐答案

@Slauma 在上面的评论,但要求我发布答案。

@Slauma provided this answer in the above comments but asked me to post the answer.

问题是实体框架的自我跟踪实体模板中有一个错误更长的建议你使用)。专门针对此主题的博文可以在这里找到

The problem is that there is effectively a "bug" in the Self-Tracking Entities templates for Entity Framework (something Microsoft no longer recommends you use). A blog post specifically on this topic can be found here.

具体来说,问题是Context的 ObjectStateManager 与(附属)实体的 ChangeTracker.State ,并且最终使用 entity.ChangeTracker.State == ObjectState.Deleted 的对象,但是当 context.ObjectStateManager 认为状态设置为 EntityState.Unchanged 。这两个显然是非常不同的。所以这个修复有效地去寻找附加到上下文的任何对象作为 EntityState.Unchanged ,但是深入挖掘并检查每个对象的 ChangeTracker.State for ObjectState.Deleted 来解决问题。

Specifically, the problem is that the Context's ObjectStateManager gets out of sync with the (attached) entities' ChangeTracker.State and you end up having objects with entity.ChangeTracker.State == ObjectState.Deleted but when the context.ObjectStateManager thinks that the state is set to EntityState.Unchanged. These two are clearly very different. So this fix effectively goes and looks for any object attached to the context as EntityState.Unchanged but digs down deeper and also checks each object's ChangeTracker.State for ObjectState.Deleted to fix things up.

一个简单而非常彻底的功能通过使用以下代码替换 #region句柄初始实体状态块,可以在Context的T4模板中进行解决这个问题(对我们有效的)

An easy and very thoroughly-functional work-around for this problem (that has worked well for us) can be made in the Context's T4 template by replacing the #region Handle Initial Entity State block with the following code:

#region Handle Initial Entity State

var existingEntities = context
    .ObjectStateManager
    .GetObjectStateEntries(System.Data.EntityState.Unchanged)
    .Select(x => x.Entity as IObjectWithChangeTracker)
    .Where(x => x != null);

var deletes = entityIndex.AllEntities
                    .Where(x => x.ChangeTracker.State == ObjectState.Deleted)
                    .Union(existingEntities
                            .Where(x => x.ChangeTracker.State == ObjectState.Deleted));

var notDeleted = entityIndex.AllEntities
                    .Where(x => x.ChangeTracker.State != ObjectState.Deleted)
                    .Union(existingEntities
                            .Where(x => x.ChangeTracker.State != ObjectState.Deleted));

foreach (IObjectWithChangeTracker changedEntity in deletes)
{
    HandleDeletedEntity(context, entityIndex, allRelationships, changedEntity);
}

foreach (IObjectWithChangeTracker changedEntity in notDeleted)
{
    HandleEntity(context, entityIndex, allRelationships, changedEntity);
}

#endregion

这篇关于无法更改关系,因为一个或多个外键属性不可为空的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆