25

我的表中有一個表具有唯一鍵,當我嘗試插入重複記錄時,它會按預期拋出異常。但是我需要區分唯一的關鍵異常和其他異常,以便我可以自定義錯誤消息來解決唯一的關鍵約束違規問題。如何使用EF6和SQL Server捕獲UniqueKey違例異常?

我在網上找到

所有的解決方案,建議投ex.InnerExceptionSystem.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.InnerExceptionSystem.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,我假設你'使用ObjectContext API,但它應該是相似的。

+0

查看您分享的代碼後,我現在可以看到我的代碼問題非常明顯。我應該寫「ex.InnerException.InnerException as 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 
    } 
} 

編輯:

你也只是檢查異常的消息組件。事情是這樣的:

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

不幸的是,catch(SqlException ex)沒有捕獲到唯一鍵違例異常並引發此錯誤:在EntityFramework.dll中發生類型爲「System.Data.Entity.Infrastructure.DbUpdateException」的異常,但未在用戶中處理代碼 –

+1

什麼錯誤?第二個如果條件? –

+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唯一索引約束違規。