ExecuteStoreCommand“ Delete”在删除后返回不同的记录数到“ DeleteObject”,为什么?

时间:2019-04-11 00:10:19

标签: entity-framework entity-framework-6

这里有一个奇怪的人。我在SQL Server 2012和C#上使用EF 6。

如果我使用DeleteObject删除记录,则会得到:

        //order.orderitem count = 11

        db.OrderItem.DeleteObject(orderitem);  
        db.SaveChanges();

        var order = db.order.First(r => r.Id == order.id);

        //Order.OrderItem count = 10, CORRECT

如果我使用ExecuteStoreCmd内联DML删除订单项,则会得到:

        //order.orderitem count = 11

        db.ExecuteStoreCommand("DELETE FROM ORDERITEM WHERE ID ={0}", orderitem.Id);

        var order = db.Order.First(r => r.Id == order.id);

        //order.orderitem count = 11, INCORRECT, should be 10

因此ExecuteStoreCommand版本报告11,但是肯定从数据库中删除了OrderItem,因此它应该报告10。另外,我还以为First()会进行Eager搜索,从而重新填充了“ order.orderitem”集合。 >

有什么想法为什么会这样?谢谢。

编辑:我正在使用ObjectContext

EDIT2:这是我使用“分离”时最接近的工作解决方案。有趣的是,“分离”实际上需要大约2秒钟!不知道它在做什么,但是它可以工作。

db.ExecuteStoreCommand("DELETE FROM ORDERITEM WHERE ID ={0}", orderitem.Id);
db.detach(orderitem);

重新查询和重新填充数据集会更快。如何强制重新查询?我认为以下可以做到:

 var order = db.order.First(r => r.Id == order.id);

EDIT3:这似乎可以强制刷新后删除,但仍然需要大约2秒钟:

db.Refresh(RefreshMode.StoreWins,Order.OrderItem);

我仍然不是很明白为什么不能只作为Order重新查询。First(r => r.id == id)类型查询通常花费不到2秒的时间。

1 个答案:

答案 0 :(得分:1)

这可能是因为执行ExecuteStoredCommand时上下文已经知道Order及其订单项。 EF不知道该命令与Order的任何缓存副本有关,因此该命令将发送到数据库,但不会更新任何已加载的实体状态。在这里,因为第一个将查找任何已加载的OrderItem,并在被告知将其从DbSet中删除时,它将查找任何引用该订单项的已加载实体。

如果您不想在删除之前确保已加载实体,则需要检查是否已加载实体,并刷新或分离其关联的引用。

如果订单项代表一个实体,则应该可以使用:

db.OrderItems.Remove(orderitem);

如果已加载订单,则订单项应自动删除。如果未加载订单,则不会丢失,稍后会在请求时从数据库加载订单,并从数据库加载订单项集。

但是,如果要使用SQL执行方法,则分离任何本地实例都应将其从本地缓存中删除。

db.ExecuteStoreCommand("DELETE FROM ORDERITEM WHERE ID ={0}", orderitem.Id);
var existingOrderItem = db.OrderItems.Local.SingleOrDefault(x => x.Id == orderItem.Id);
if(existingOrderItem != null)
    db.Entity(existingOrderItem).State = EntityState.Detached;

我不认为您需要检查orderItem的“订单”以刷新除此以外的任何内容,但是我不确定100%是否对此表示满意。通常,尽管要修改数据状态,但我还是选择加载适用的顶级实体并删除其子代。

因此,如果我有命令从订单中删除订单商品:

public void RemoveOrderItem(int orderId, int orderItemId)
{
    using (var context = new MyDbContext())
    {
        // TODO: Validate that the current user session has access to this order ID
        var order = context.Orders.Include(x => x.OrderItems).Single(x => x.OrderId == orderId);
        var orderItem = order.OrderItems.SingleOrDefault(x => x.OrderItemId == orderItemId);
        if (orderItem != null)
            order.OrderItems.Remove(orderItem);

        context.SaveChanges();
    }
}

这种方法的关键点。

  • 这确实意味着要再次为该操作加载数据状态,但此加载是通过ID进行的,因此速度很快。
  • 我们可以/应该验证所请求的数据是否适用于用户。有关他们不应访问的命令的任何命令都应记录下来,会话结束。
  • 我们知道我们将处理当前的数据状态,而不是基于从首次读取数据的时间点开始的值/数据的决定。