c# sql-server exception-handling entity-framework-6 unique-key

c# - ¿Cómo puedo detectar las excepciones de Violación UniqueKey con EF6 y SQL Server?



sql-server exception-handling (5)

Con EF6 y la API DbContext (para SQL Server), actualmente estoy usando este fragmento de código:

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... }

Como mencionó UpdateException , supongo que está utilizando la API ObjectContext , pero debería ser similar.

Una de mis tablas tiene una clave única y cuando intento insertar un registro duplicado arroja una excepción como se esperaba. Pero necesito distinguir excepciones de clave únicas de otras, de modo que pueda personalizar el mensaje de error para violaciones de restricción de clave únicas.

Todas las soluciones que he encontrado en línea sugieren lanzar ex.InnerException a System.Data.SqlClient.SqlException y verificar si la propiedad Number es igual a 2601 o 2627 de la siguiente manera:

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."; } }

Pero el problema es que la conversión de ex.InnerException a System.Data.SqlClient.SqlException produce un error de ex.InnerException no válido ya que ex.InnerException es en realidad el tipo de System.Data.Entity.Core.UpdateException , no System.Data.SqlClient.SqlException .

¿Cuál es el problema con el código de arriba? ¿Cómo puedo detectar violaciones de Restricción de clave única?


En mi caso, estoy usando EF 6 y decoré una de las propiedades en mi modelo con:

[Index(IsUnique = true)]

Para detectar la violación hago lo siguiente, usando C # 7, esto se vuelve mucho más fácil:

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(); }

Tenga en cuenta que esto solo detectará una violación de restricción de índice única.


Si quieres capturar una restricción única

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


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

EDITAR:

También podría simplemente inspeccionar el componente de mensaje de la excepción. Algo como esto:

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


try { // do your insert } catch(Exception ex) { if (ex.GetBaseException().GetType() == typeof(SqlException)) { Int32 ErrorCode = ((SqlException)ex.InnerException).Number; switch(ErrorCode) { case 2627: // Unique constraint error break; case 547: // Constraint check violation break; case 2601: // Duplicated key row error break; default: break; } } else { // handle normal exception } }