ExecuteStoreCommand «Удалить», возвращающий различное количество записей в «DeleteObject» после удаления, почему? - PullRequest
0 голосов
/ 11 апреля 2019

Странно здесь. Я использую EF 6 поверх SQL Server 2012 и C #.

Если я удаляю запись, используя 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

Если я удаляю позицию заказа, используя встроенный DML ExecuteStoreCmd, я получаю:

        //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

РЕДАКТИРОВАТЬ 2: Это самое близкое рабочее решение, которое я использовал, используя "отсоединить". Интересно, что «отсоединение» на самом деле занимает около 2 секунд! Не уверен, что он делает, но это работает.

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

Было бы быстрее запросить и снова заполнить набор данных. Как я могу форсировать запрос? Я думал, что следующее сделает это:

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

РЕДАКТИРОВАТЬ3: кажется, работает, чтобы принудительно удалить сообщение обновления, но все еще занимает около 2 секунд:

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

Я все еще не совсем понимаю, почему нельзя просто запросить запрос типа Order.First (r => r.id == id), который часто занимает намного меньше 2 секунд.

1 Ответ

1 голос
/ 11 апреля 2019

Вероятно, это связано с тем, что Order и его элементы уже известны контексту при выполнении ExecuteStoredCommand.EF не знает, что команда относится к какой-либо кэшированной копии Order, поэтому команда будет отправлена ​​в базу данных, но не обновит любое загруженное состояние объекта.WHERE - как первый будет искать любой загруженный OrderItem, а когда будет сказано удалить его из DbSet, он будет искать любые загруженные объекты, которые ссылаются на этот элемент заказа.

Если вы не хотите гарантироватьобъект (ы) загружаются перед удалением, тогда вам нужно будет проверить, загружены ли какие-либо объекты, и обновить или отсоединить связанные с ними ссылки.

Если orderitem представляет объект, он должен просто иметь возможность использовать:

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's Order, чтобы обновить что-либо кроме этого, но я не уверен на 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();
    }
}

Ключевые моменты этого подхода.

  • Хотя это означает загрузку состояния данных для операции, эта загрузка выполняется по идентификатору, поэтому она быстрая.
  • Мы можем/ должен подтвердить, что запрашиваемые данные применимы для пользователя.Любая команда для заказа, к которому они не должны обращаться, должна быть зарегистрирована и сеанс завершен.
  • Мы знаем, что будем иметь дело с текущим состоянием данных, а не на основе решений о значениях / данных с момента времени, когда данные былипервое чтение.
...