【问题标题】:error Removing child entity; deleted child entity is marked as Modified instead of Deleted错误删除子实体;已删除的子实体被标记为已修改而不是已删除
【发布时间】:2019-03-17 19:24:11
【问题描述】:

我在尝试从父单数据库记录中删除子实体时遇到了意外问题。 在做了一些测试之后,我们已经复制了这个问题。 我们的 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_DetailOrders 属性设置为 null 并将记录标记为 Updated

我发现很多关于这个问题的文章,都建议将Order_Detail Orders属性标记为[Required]

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

这是预期的行为吗?

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

谢谢

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    AFAIK 这是正确的行为。

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

    • 集合已更新,
    • 导航属性设置为空。

    从逻辑上讲,如果你SaveChanges 你应该有一个例外,因为没有订单就不能存在细节,而且你还没有删除细节,只是切断关系。所以你应该ctx.Set&lt;orderDetail&gt;().Remove(detail)。 (有点烦)

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

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

    【讨论】:

    • 您好,感谢您的快速回答。我们试图用 EF6.2 模型替换旧的 L2S 模型;在我们的解决方案上使用ctx.Set&lt;orderDetail&gt;().Remove(detail) 更改Order.Remove(detail) 方法将做一项昂贵的工作。你知道一个快速的方法来拦截和管理DbContext这个问题吗?谢谢
    • 要使用复合键,FK 应该是 PK 的一个组件,这样:Remove => (FK = null PK = null) => 从 db 中删除
    • 不幸的是,这对我们来说不是一个选择,因为我们需要在两种不同的解决方案上保持旧的 L2S ORM 和 EF 保持活力。在这种情况下,较旧的 L2S 无法将 FK 作为 PK 的组件正确管理。你认为在DbContext 级别上工作应该是一种选择吗?
    • 不确定是否理解,但您可以覆盖 SaveChanges 以查找/更新上下文以删除带有空 FK 的详细信息。根据您的模型大小,它是否可扩展/可维护。
    【解决方案2】:

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

    Orders 实体上,我们使用了 ObservableListSource 而不是 ICollection;进入 ObservableListSource 类,void RemoveItem 覆盖方法,我们可以管理ctx.Set&lt;orderDetail&gt;().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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多