34

我使用实体框架 4,并且我与“级联删除”设置有父子关系。所以我希望当我从父级中删除一个子级时,当我调用 SaveChanges() 时,该子级会被删除。

        cuRepository.Attach(_controlUnit);
        foreach (var recipe in recipes) {
            _controlUnit.Recipes.Remove(recipe);
            //repository.DeleteObject(recipe);
        }

相反,我得到一个错误:

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

当我明确删除孩子(见注释行)时,一切都很好。我错过了什么?

4

5 回答 5

30

您没有使用 remove 语句删除对象。相反,您正在尝试更改记录并使其成为孤立记录(通过将外键设置为空)。数据库对该列具有非空约束,并阻止您这样做。

于 2010-03-31T16:37:40.197 回答
27

http://weblogs.asp.net/zeeshanhirani/archive/2010/07/23/removing-entity-from-a-related-collection.aspx准确地解释了发生在你身上的事情。


假设你有一个这样的类设计:

样本类设计

Entity Framework 将生成所需的外键列NOT NULL并向它们添加约束,因为所有配方将始终与一个 ControlUnit 相关联。

因此,在运行时,您将拥有类似于以下布局的对象:

运行时对象图

现在您的代码开始发挥作用并删除了 Recipe 对象与其 ControlUnit 之间的关系:

具有已删除关系的对象

此时尝试保存,数据库没有要放入外键NOT NULL列的 ControlUnit ID。当前对象状态违反了上面的类图,并且无法保存到在每个配方与一个 ControlUnit 相关联的假设下生成的数据库布局中。这就是数据库拒绝保存更改并且您看到异常的原因。

这也解释了为什么当您取消注释删除实体的行时它会起作用:实体连同其关系一起从数据库中删除,因此没有违反约束,因此也不例外。

“可是我开始ON DELETE CASCADE谈恋爱了……”

是的,但这仅在删除对象时触发,而不是在删除关系时触发。使用ON DELETE CASCADEset,这应该可以工作:

controlUnitRepository.DeleteObject(_controlUnit);
// deletes the ControlUnit and all associated Recipe entities

如果你想在删除它们与 ControlUnit 的关系时触发删除 Recipe 实体,你的关系不应该是一个简单的关联,而是一个组合:

用组合更新了类图

EF 本身不支持这一点,但您可以使用标识关系来模拟行为。一旦实体与父实体处于识别关系并且该关系被移除,该实体也被移除。这似乎是你从一开始的意图。有关识别关系的详细信息,请参阅实现识别与 EF4 的关系,其中我实现了识别与 EF4 的关系,并链接到更多阅读材料。

于 2010-09-14T15:17:03.733 回答
11

context.DeleteObject(recipe)在循环内添加

于 2010-03-31T16:39:30.197 回答
8

如果您将子实体与父实体之间的关系作为识别关系,则可以从集合中删除子实体。您需要使子键成为包含父主 id 键的复合键。这样 EF 就知道它需要移除孩子。

识别关系基本上是说如果父母不存在,那么孩子就没有意义。这意味着 EF 知道在删除关系时删除子项是安全的。

请参阅这个问题识别关系和插入子实体导致“无法在表中插入标识列的显式值”和这个问题是否可以从集合中删除子实体并解决 SaveChanges 上的问题?

于 2012-06-20T00:08:39.190 回答
3

我使用这个扩展是为了不在 DAL 中添加一个方法只是为了删除一个实体(代码取自 http://blogs.msdn.com/b/alexj/archive/2009/06/08/tip-24-how- to-get-the-objectcontext-from-an-entity.aspx):

public static void Delete<T>(this EntityCollection<T> collection, T entityToDelete) where T : EntityObject, IEntityWithRelationships
{
    RelationshipManager relationshipManager = entityToDelete.RelationshipManager;

    IRelatedEnd relatedEnd = relationshipManager.GetAllRelatedEnds().FirstOrDefault();
    if (relatedEnd == null)
    {
        throw new Exception("No relationships found for the entity to delete. Entity must have at least one relationship.");
    }

    var query = relatedEnd.CreateSourceQuery() as ObjectQuery;
    if (query == null)
    {
        throw new Exception("The entity to delete is detached. Entity must be attached to an ObjectContext.");
    }

    query.Context.DeleteObject(entityToDelete);
    collection.Remove(entityToDelete);
}

所以我然后删除一个实体,如Order.Products.Delete(prod).

使用扩展的约束是:
- 实体必须有关系;
- 实体必须附加到 ObjectContext。

于 2012-06-11T12:12:59.673 回答