25

В одной из моих таблиц есть уникальный ключ, и когда я пытаюсь вставить дублируемую запись, она генерирует исключение, как ожидалось. Но мне нужно отличать уникальные исключения ключей от других, так что я могу настроить сообщение об ошибке для уникальных нарушений ограничения ключа.Как я могу исключить исключения исключения UniqueKey с EF6 и SQL Server?

Все решения, которые я нашел в Интернете предлагает бросить ex.InnerException к System.Data.SqlClient.SqlException и проверить, если Number свойство равно 2601 или 2627 следующим образом:

try 
{ 
    _context.SaveChanges(); 
} 
catch (Exception ex) 
{ 
    var sqlException = ex.InnerException as System.Data.SqlClient.SqlException; 

    if (sqlException.Number == 2601 || sqlException.Number == 2627) 
    { 
     ErrorMessage = "Cannot insert duplicate values."; 
    } 
    else 
    { 
     ErrorMessage = "Error while saving data."; 
    } 
} 

Но проблема в том, отбрасывая ex.InnerException в System.Data.SqlClient.SqlException причины неверная ошибка при запуске с ex.InnerException на самом деле тип System.Data.Entity.Core.UpdateException, а не System.Data.SqlClient.SqlException.

В чем проблема с кодом выше? Как я могу уловить нарушения уникального ключа?

ответ

38

С EF6 и DbContext API (для SQL Server), я в настоящее время использую этот кусок кода:

try 
{ 
    // Some DB access 
} 
catch (Exception ex) 
{ 
    HandleException(ex); 
} 

public virtual void HandleException(Exception exception) 
{ 
    if (exception is DbUpdateConcurrencyException concurrencyEx) 
    { 
    // A custom exception of yours for concurrency issues 
    throw new ConcurrencyException(); 
    } 
    else if (exception is DbUpdateException dbUpdateEx) 
    { 
    if (dbUpdateEx.InnerException != null 
      && dbUpdateEx.InnerException.InnerException != null) 
    { 
     if (dbUpdateEx.InnerException.InnerException is SqlException sqlException) 
     { 
     switch (sqlException.Number) 
     { 
      case 2627: // Unique constraint error 
      case 547: // Constraint check violation 
      case 2601: // Duplicated key row error 
         // Constraint violation exception 
      // A custom exception of yours for concurrency issues 
      throw new ConcurrencyException(); 
      default: 
      // A custom exception of yours for other DB issues 
      throw new DatabaseAccessException(
       dbUpdateEx.Message, dbUpdateEx.InnerException); 
     } 
     } 

     throw new DatabaseAccessException(dbUpdateEx.Message, dbUpdateEx.InnerException); 
    } 
    } 

    // If we're here then no exception has been thrown 
    // So add another piece of code below for other exceptions not yet handled... 
} 

Как вы упомянули UpdateException, я предполагаю, что ты используя API ObjectContext, но он должен быть аналогичным.

+0

После проверки кода, который вы поделили, я теперь вижу, что проблема с моим кодом настолько очевидна. Я должен написать «ex.InnerException.InnerException как SqlException» вместо «ex.InnerException as SqlException». –

+1

Есть ли способ обнаружить также, на каком нарушении произошло нарушение столбца? В одной таблице может быть несколько уникальных ключей ... – Learner

+0

@Learner Единственный способ, которым я могу думать, - разобрать сообщение об ошибке (в котором указано имя ограничения/столбца), но это было бы не очень хорошо (сообщения об ошибках могут быть обновлены в будущем и, что более важно, переведены на несколько языков) – ken2k

5
// put this block in your loop 
try 
{ 
    // do your insert 
} 
catch(SqlException ex) 
{ 
    // the exception alone won't tell you why it failed... 
    if(ex.Number == 2627) // <-- but this will 
    { 
     //Violation of primary key. Handle Exception 
    } 
} 

EDIT:

Вы также можете просто проверить компонент сообщения исключения. Что-то вроде этого:

if (ex.Message.Contains("UniqueConstraint")) // do stuff 
+3

К сожалению, catch (SqlException ex) не улавливает исключительное исключение исключительного ключа и выдает эту ошибку: исключение типа «System.Data.Entity.Infrastructure.DbUpdateException» произошло в EntityFramework.dll, но не было обработано пользователем код –

+1

какая ошибка? и второй с условием if? –

+0

Проверка «UniqueConstraint» в сообщении об ошибке должна работать, но, похоже, это не лучший подход. –

2

Если вы хотите, чтобы поймать уникальное ограничение

try { 
    // code here 
} 
catch(Exception ex) { 
    //check for Exception type as sql Exception 
    if(ex.GetBaseException().GetType() == typeof(SqlException)) { 
    //Violation of primary key/Unique constraint can be handled here. Also you may //check if Exception Message contains the constraint Name 
    } 
} 
3

В моем случае, я использую EF 6 и оформлены одним из свойств в моей модели с:

[Index(IsUnique = true)] 

поймать нарушение Я делаю следующее, используя C# 7, это становится намного проще:

protected async Task<IActionResult> PostItem(Item item) 
{ 
    _DbContext.Items.Add(item); 
    try 
    { 
    await _DbContext.SaveChangesAsync(); 
    } 
    catch (DbUpdateException e) 
    when (e.InnerException?.InnerException is SqlException sqlEx && 
    (sqlEx.Number == 2601 || sqlEx.Number == 2627)) 
    { 
    return StatusCode(StatusCodes.Status409Conflict); 
    } 

    return Ok(); 
} 

Обратите внимание, что это будет только кошка ch уникальное ограничение ограничения индекса.

Смежные вопросы