操作失败:无法更改关系,因为一个或多个外键属性不可为空。

时间:2016-06-29 16:58:17

标签: c# entity-framework linq

此问题可能与其他类似问题重复。但我建议你阅读这个问题直到最后,然后决定它是否与某些帖子重复??????

我的数据库中有6个表,如下所示:

enter image description here

我已经在所有表中插入了一些记录。

现在,我正在尝试更新订单。

首先,我只是尝试更新订单,如下所示:

CurrentOrder.UpdateOrder(Order);

OrderClient中的UpdateOrder方法如下所示:

public Order UpdateOrder(Order Order)
{
    IOrderRepository OrderRepository = _DataRepositoryFactory.GetDataRepository<IOrderRepository>();

    Order updatedEntity = null;

    if (Order.OrderId == 0)
    {
        updatedEntity = OrderRepository.Add(Order);
    }
    else
    {
        updatedEntity = OrderRepository.Update(Order);
    }

    return updatedEntity;

}

在OrderRepository中:

protected override Order UpdateEntity(RateDifferenceContext entityContext, Order entity)
{
    return (from e in entityContext.OrderSet
            where e.OrderId == entity.OrderId
            select e).FirstOrDefault();
}

然后在DataRepositoryBase类中我使用以下方法:

public T Update(T entity)
{
    using (U entityContext = new U())
    {
        T existingEntity = UpdateEntity(entityContext, entity);
        SimpleMapper.PropertyMap(entity, existingEntity);
        entityContext.SaveChanges();
        return existingEntity;
    }
}

此时我收到一条错误消息:

  

违反了多重性约束。关系'...'的作用'...'具有多重性1或0..1

所以,我认为我需要在所有相关表中删除特定于此顺序的记录。所以,我尝试了以下代码:

using (var xaction = new TransactionScope())
{
    foreach (OrderItemDetail orderItemDetail in OrderItemDetailClient.GetAllOrderItemDetails().Where(x => x.OrderId == NewOrder.OrderId))
    {
        OrderItemDetailClient.DeleteOrderItemDetail(orderItemDetail);
    }

    foreach (Dispatch dispatch in DispatchClient.GetAllDispatches().Where(x => x.OrderId == NewOrder.OrderId))
    {
        foreach (DispatchItemDetail dispatchItemDetail in DispatchItemDetailClient.GetAllDispatchItemDetails().Where(x => x.InvoiceId == dispatch.InvoiceId))
        {
            DispatchItemDetailClient.DeleteDispatchItemDetail(dispatchItemDetail);
        }

        DispatchClient.DeleteDispatch(dispatch);
    }

    OrderClient.UpdateOrder(NewOrder);

    xaction.Complete();
}

现在,我得到另一个错误说:

  

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

我在上一个代码块中的下面提到的行中出现此错误:

DispatchClient.DeleteDispatch(dispatch);

1 个答案:

答案 0 :(得分:5)

你有两个不同的问题。我们没有足够的细节来为您提供具体的解决方案,但由于这些都是非常常见的EF“陷阱”,我相信了解正在发生的事情是很有价值的。

第一个错误:

  

违反了多重性约束。关系'...'的作用'......'   具有多重性1或0..1

这意味着您的外键属性不匹配。在EF中,您经常会遇到在模型中以多种方式表示相同SQL关系的情况。

例如,您的Order类上可能有一个OrderItemDetails集合(使用OrderItemDetail.OrderId填充)。您的OrderItemDetail也可能有一个Order属性,使用相同的外键填充。如果这两个属性都标记为已更改但新值不匹配,则EF不知道要保存到OrderItemdetail.OrderId字段的新值。在那种情况下,它将抛出此异常。如果OrderItemDetail具有Order属性和OrderId属性,则会出现同样的问题。

要避免此问题,您必须非常小心修改了哪些属性。使用属性映射器可能很危险,因为它们可能“意外地”修改错误的属性并导致许多类似的问题。我们需要看看SimpleMapper如何工作或如何配置映射以进行真正的故障排除。

第二个错误:

  

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

此错误通常意味着您并未真正删除对象。您正在从关系集合中删除该对象,该集合仅将外键设置为null。

继续上面的例子,如果你调用myOrder.OrderItemDetails.Remove(detail)然后调用SaveChanges,你可能会认为它只会从数据库中删除OrderItemDetail记录,但这并不是你告诉它要做的。您已从与 myOrder关联的订单商品详情列表中删除了该商品。为了实现这一点,EF生成一个UPDATE语句,将OrderId列设置为null。如果没有关于模型和DeleteDispatch方法代码的更多细节,很难确切地知道问题的位置,但异常意味着它试图将该外键属性设置为null并失败,因为它不可为空。

“修复”是直接从Context集合中删除项目而不是相关项目集合。即而不是myOrder.OrderItemDetails.Remove,你应该调用context.OrderItemDetails.Remove。这会删除真实的记录。