Ядро Entity Framework: невозможно поймать SqlException - PullRequest
0 голосов
/ 25 августа 2018

Я пытаюсь реализовать оптимистическую блокировку при вставке строк в таблицу базы данных.

Я пытаюсь получить строку из базы данных. Если строка с данным ключом не существует, она вставляется первой. В настоящее время я тестирую это для сценариев с высоким уровнем параллелизма.

Идея такова: если строка уже была добавлена ​​другим потоком, исключение перехватывается, и объект перечитывается из базы данных. Тем не менее, я не могу поймать исключение.

Вот метод, о котором идет речь

    private async Task<Counter> AcquireCounterAsync(string resType)
    {
        var repo = _dbContext.Set<Counter>();

        while (true)
        {
            var ctr = await repo.FindAsync(resType);

            if (ctr != null)
                return ctr;

            ctr = new Counter(resType);

            try
            {
                Console.WriteLine("Trying to add " + resType);
                repo.Add(ctr);
                await _dbContext.SaveChangesAsync();
                Console.WriteLine("Created " + resType);
                return ctr;
            }
            catch (SqlException ex)
            {
                // this block is never entered
                Console.WriteLine("SqlException was thrown: " + ex.Message);
                continue;
            }
        }            
    }

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

Trying to add test/test0
Trying to add test/test0
Created test/test0
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (157ms) [Parameters=[@p0='?' (Size = 128), @p1='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
      SET NOCOUNT ON;
      INSERT INTO [counters] ([ResourceType], [Value])
      VALUES (@p0, @p1);
System.Data.SqlClient.SqlException (0x80131904): Verletzung der PRIMARY KEY-Einschränkung 'PK_counters'. Ein doppelter Schlüssel kann in das dbo.counters-Objekt nicht eingefügt werden. Der doppelte Schlüsselwert ist (test/test0).
Die Anweisung wurde beendet.
   at System.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__122_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteAsync(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues, CancellationToken cancellationToken)
ClientConnectionId:d1f14d55-c95b-4c19-b1bc-468241df225d
Error Number:2627,State:1,Class:14

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

РЕДАКТИРОВАТЬ: Я пытался сделать этот метод тоже синхронным, тот же результат, просто чуть более длинный след стека, идущий вниз к вызову в концентраторе SignalR. Так что это определенно не пойман.

МОЕ ИСПРАВЛЕНИЕ: Для пояснения: я исправил это, перехватив «DbUpdateException» И сделав все методы синхронными. Я все еще не могу поймать «DbUpdateException» или даже «Exception», если я использую асинхронные методы. Так что это только частично связано с этим вопросом: Невозможно отловить SqlException в Entity Framework

1 Ответ

0 голосов
/ 25 августа 2018

Я настроил небольшую демонстрацию, подобную этой

    var uid = Guid.NewGuid();
    using (var context = new DemoContext())
    {
       context.Stuff.Add(new Stuff { Uid = uid, Data = "stuff1" });
       await context.SaveChangesAsync();
    }
    using (var context = new DemoContext())
    {
       context.Stuff.Add(new Stuff { Uid = uid, Data = "stuff2" });
       await context.SaveChangesAsync();
    }

Это обернуто в блок try ... catch, и я могу поймать выброшенное исключение, но оно имеет тип Microsoft.EntityFrameworkCore.DbUpdateException с System.Data.SqlClient.SqlException как будто вы завернуты внутрь.

Вы ловите где-то еще и выбрасываете только внутреннее исключение?Попробуйте поймать Exception в своем коде, просто чтобы убедиться.

...