如何使用EF6和SQL Server捕获UniqueKey违例异常?
我的表中有一个表具有唯一键,当我尝试插入重复记录时,它会按预期抛出异常。但是我需要区分唯一的关键异常和其他异常,以便我可以自定义错误消息来解决唯一的关键约束违规问题。如何使用EF6和SQL Server捕获UniqueKey违例异常?
我在网上找到所有的解决方案,建议投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
。
上面的代码有什么问题?我如何捕获唯一键约束违规?
随着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,但它应该是相似的。
// 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
不幸的是,catch(SqlException ex)没有捕获到唯一键违例异常并引发此错误:在EntityFramework.dll中发生类型为“System.Data.Entity.Infrastructure.DbUpdateException”的异常,但未在用户中处理代码 –
什么错误?第二个如果条件? –
检查错误信息中的“UniqueConstraint”应该可行,但它似乎不是最好的方法。 –
如果你想赶上唯一约束
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
}
}
就我而言,我使用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唯一索引约束违规。
查看您分享的代码后,我现在可以看到我的代码问题非常明显。我应该写“ex.InnerException.InnerException as SqlException”而不是“ex.InnerException as SqlException”。 –
有没有办法来检测哪个列违规发生?在一个表中可能有多个唯一键... – Learner
@Learner我能想到的唯一方法是解析错误消息(其中声明约束/列的名称),但它不会是一个很好的解决方案(错误消息将来可能会更新,更重要的是,会翻译成多种语言) – ken2k