Ef Core 2.0 - Добавление объектов после удаления данных на стороне сервера вызывает исключения и дублирует данные - PullRequest
0 голосов
/ 03 июля 2018

Я обнаружил интересную проблему с отслеживанием изменений ядра EF и с тем, как он добавляет ненужные дубликаты данных в мою базу данных.

Порядок операций:

  1. Добавить новую сущность "Заказать"
  2. Удалить заказ с помощью ручного запроса к базе данных
  3. Добавить еще одну новую сущность "Заказ"
  4. Выдается исключение: The instance of entity type 'Order' cannot be tracked because another instance with the same key value for {'OrderId'} is already being tracked. (новая сущность все еще добавляется в ChangeTracker)
  5. Добавить еще одну новую сущность "Заказ"
  6. В этот раз это сработало, но в него были добавлены два новых ордера, и теперь есть повторяющиеся данные.

Я не использую это в веб-приложении, это сервис, который поддерживает единый контекст соединения, потому что он постоянно обрабатывает очередь данных. Но я должен признать, что данные могут быть удалены за пределами этого контекста / службы.

* * Пример тысяча двадцать-один: * * 1 022
public async Task TestException()
{
    var dirtyOrders = _allOrders.Where(x => x.IsDirty).ToList();

    foreach(var order in dirtyOrders)
    {
        await CreateAsync(order);
        order.IsDirty=false;
    }

    Sql("TRUNCATE dbo.Orders;");

    dirtyOrders = _allOrders.Where(x => x.IsDirty).ToList();

    foreach(var order in dirtyOrders)
    {
        await CreateAsync(order); // exception thrown
        order.IsDirty=false;
    }

}

public async Task<Order> CreateAsync(Order order)
{
    _dbContext.OrderBook.Add(order);
    await _dbContext.SaveChangesAsync();
    return order;
}

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Я понял, в чем проблема. Поскольку я использовал Truncate, чтобы стереть данные, он сбрасывал последовательность автоинкремента на 1. Порядок # 1 уже был в ChangeTracker после усечения, и когда он пошел на вставку другого ордера, БД возвратила первичный ключ '1', который уже присутствовал в ChangeTracker. Последующие вызовы могут обновить средство отслеживания изменений, но ваша сущность все еще находится там в состоянии «добавлено», несмотря на исключение, и в будущем при вызове этой же записи и вызове SaveChanges теперь будут вставлены две повторяющиеся записи.

Хотя я могу избежать этого состояния, не сбрасывая последовательности, нет простого способа предотвратить запуск EF в этом состоянии. Я думаю, что поведение должно состоять в том, чтобы перезаписать запись в Changetracker в состоянии «Добавлено», а не генерировать исключение. Исключение не содержит ключ, который находился в конфликте, потому что он был получен с сервера, поэтому нет хорошего способа программно с ним справиться.

По крайней мере, я могу удалить его из changeTracking, если SaveChangesAsync () не может предотвратить появление повторяющихся строк в будущем.

public async Task<Order> CreateAsync(Order order)
{
    _dbContext.OrderBook.Add(order);
    try
    {
        await _dbContext.SaveChangesAsync();
    }
    catch(DbUpdateException ex)
    {
        _dbContext.OrderBook.Remove(order);
        throw ex;
    }
    return order;
}
0 голосов
/ 03 июля 2018

Вы можете создать отдельный метод для удаления вашей модели, чтобы избежать жизненного цикла и отслеживания модели EF. надеюсь, что это сработает.

private bool DeleteModel(Order order)
{
    try
    {
        _context.Database.ExecuteSqlCommand($"DELETE Order WHERE Id={order.Id}");
        _context.SaveChanges();
        return true;
    }
    catch(Exception ex)
    {
        return false;
    }
}


public bool SaveOrder(Order order)
{
    try
    {
        //Delete Model First
        if(DeleteModel(order))
        {
            _context.Orders.Add(order);
            _context.SaveChanges();
            return true;
        }
        else
        {
            return false;
        }
    }
    catch(Exception ex)
    {
        return false;
    }
}
...