错误删除子实体;删除的子实体标记为“已修改”,而不是“已删除”

时间:2019-03-17 19:24:11

标签: c# entity-framework

我尝试从父单数据库记录中删除子实体时遇到意外问题。 经过一些测试,我们已经复制了该问题。 我们的C#代码使用Northwind数据库。

NWModel context = new NWModel();
Orders order = context.Orders.Where(w => w.OrderID == 10248).FirstOrDefault();
context.Entry(order).Collection(typeof(Order_Details).Name).Load();
order.Order_Details.RemoveAt(1);
System.Data.Entity.Infrastructure.DbChangeTracker changeset = context.ChangeTracker;
var changes = changeset.Entries().Where(x => x.State == System.Data.Entity.EntityState.Modified).ToList<Object>();
var detetions = changeset.Entries().Where(x => x.State == System.Data.Entity.EntityState.Deleted).ToList<Object>();

在原始的Order_Details表设置中,一切正常。

正确的var删除已删除记录。

为了重现该问题,我们在Order_Details表上添加了一个新的PK身份int字段(OrderDetailId int identity);之后: var删除不包含记录,而 var更改包含 Order_Detail 记录。

EF将 Order_Detail Orders 属性设置为null并将记录标记为已更新

我找到了很多有关此问题的文章,所有这些文章都建议将 Order_Detail Orders 属性标记为 [必需]

我尝试按照this post中的建议在FK实体上设置 [Required] 属性,(本文介绍了与EF6行为相同的EFCore行为),但没有解决我的问题。

这是预期的行为吗?

我们将不胜感激任何意见或建议。

谢谢

2 个答案:

答案 0 :(得分:1)

这是正确的行为。

确实,您不删除详细信息。您从订单明细集合中将其删除,即切断了订单和明细之间的关系。由于此关系是通过详细信息侧的导航属性实现的,因此发生了两件事:

  • 收藏集已更新
  • 导航属性设置为null。

从逻辑上来说,如果您SaveChanges应该有一个例外,因为没有订单就无法存在详细信息,并且您还没有删除该详细信息,只能剪切该关系。因此,您应该ctx.Set<orderDetail>().Remove(detail)。 (有点烦人)

这就是为什么在这种情况下,我通常使用复合键获取详细信息:detailId + orderId。

因此,当您删除详细信息时,会将orderId设置为null <=> PK被视为null =>将该实体标记为删除。

答案 1 :(得分:1)

按照tschmit007的建议,终于我们实现了此解决方法。

Orders 实体上,我们使用 ObservableListSource <> 代替了 ICollection <> ;放入 ObservableListSource <> 类,并使用 void RemoveItem 覆盖方法,我们可以管理ctx.Set<orderDetail>().Remove(detail)方法。 这样,所有子记录删除操作都可以按预期进行。

以下是实现的代码:

public partial class Orders
{
public Orders()
{
    Order_Details = new ObservableListSource<Order_Details>();
}
[Key]
public int OrderID { get; set; }
……………
public virtual ObservableListSource<Order_Details> Order_Details { get; set; }
}


public class ObservableListSource<T> : ObservableCollection<T>, IListSource
        where T : class
{
    private IBindingList _bindingList;

    bool IListSource.ContainsListCollection { get { return false; } }

    IList IListSource.GetList()
    {
        return _bindingList ?? (_bindingList = this.ToBindingList());
    }
    private bool _bRemoveInProgress = false;
    protected override void RemoveItem(int index)
    {
        if (!_bRemoveInProgress && index>=0)
        {
            _bRemoveInProgress = true;
            DbContext cntx = this[index].GetDbContextFromEntity();
            Type tp = this[index].GetDynamicProxiesType();
            cntx.Set(tp).Remove(this[index]);
            base.RemoveItem(index);
        }
        _bRemoveInProgress = false;
    }
}


public static class DbContextExtender
{
    public static Type GetDynamicProxiesType(this object entity)
    {
        var thisType = entity.GetType();
        if (thisType.Namespace == "System.Data.Entity.DynamicProxies")
            return thisType.BaseType;
        return thisType;
    }
    public static DbContext GetDbContextFromEntity(this object entity)
    {
        var object_context = GetObjectContextFromEntity(entity);

        if (object_context == null)
            return null;

        return new DbContext(object_context, dbContextOwnsObjectContext: false);
        //return object_context;
    }

    private static ObjectContext GetObjectContextFromEntity(object entity)
    {
        var field = entity.GetType().GetField("_entityWrapper");

        if (field == null)
            return null;

        var wrapper = field.GetValue(entity);
        var property = wrapper.GetType().GetProperty("Context");
        var context = (ObjectContext)property.GetValue(wrapper, null);

        return context;
    }
}