EF6 및 SQL Server에서 UniqueKey Violation 예외를 어떻게 탐지할 수 있습니까?
테이블 중 하나에 고유한 키가 있으며 중복 레코드를 삽입하려고 하면 예상대로 예외가 발생합니다.그러나 고유한 키 제약 조건 위반에 대한 오류 메시지를 사용자 지정할 수 있도록 고유한 키 예외를 다른 키와 구별해야 합니다.
온라인에서 찾은 모든 해결책은 캐스팅을 제안합니다.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, 하지만 비슷해야 합니다.
저의 경우 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();
}
고유한 인덱스 제약 조건 위반만 탐지됩니다.
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
}
}
// 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
중복 행 예외 처리뿐만 아니라 프로그래밍 목적으로 사용할 수 있는 유용한 정보를 추출하는 코드를 보여주는 것이 유용할 수 있다고 생각했습니다.예: 사용자 지정 메시지 작성.
이것.Exception
하위 클래스는 regex를 사용하여 db 테이블 이름, 인덱스 이름 및 키 값을 추출합니다.
public class DuplicateKeyRowException : Exception
{
public string TableName { get; }
public string IndexName { get; }
public string KeyValues { get; }
public DuplicateKeyRowException(SqlException e) : base(e.Message, e)
{
if (e.Number != 2601)
throw new ArgumentException("SqlException is not a duplicate key row exception", e);
var regex = @"\ACannot insert duplicate key row in object \'(?<TableName>.+?)\' with unique index \'(?<IndexName>.+?)\'\. The duplicate key value is \((?<KeyValues>.+?)\)";
var match = new System.Text.RegularExpressions.Regex(regex, System.Text.RegularExpressions.RegexOptions.Compiled).Match(e.Message);
Data["TableName"] = TableName = match?.Groups["TableName"].Value;
Data["IndexName"] = IndexName = match?.Groups["IndexName"].Value;
Data["KeyValues"] = KeyValues = match?.Groups["KeyValues"].Value;
}
}
그DuplicateKeyRowException
수업은 사용하기에 충분히 쉽습니다...이전 답변과 같이 오류 처리 코드를 생성합니다.
public void SomeDbWork() {
// ... code to create/edit/update/delete entities goes here ...
try { Context.SaveChanges(); }
catch (DbUpdateException e) { throw HandleDbUpdateException(e); }
}
public Exception HandleDbUpdateException(DbUpdateException e)
{
// handle specific inner exceptions...
if (e.InnerException is System.Data.SqlClient.SqlException ie)
return HandleSqlException(ie);
return e; // or, return the generic error
}
public Exception HandleSqlException(System.Data.SqlClient.SqlException e)
{
// handle specific error codes...
if (e.Number == 2601) return new DuplicateKeyRowException(e);
return e; // or, return the generic error
}
고유한 제약 조건을 적용하려는 경우
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
}
}
코드를 작성할 때는 매우 구체적이어야 합니다.
try
{
// do your stuff here.
{
catch (Exception ex)
{
if (ex.Message.Contains("UNIQUE KEY"))
{
Master.ShowMessage("Cannot insert duplicate Name.", MasterSite.MessageType.Error);
}
else { Master.ShowMessage(ex.Message, MasterSite.MessageType.Error); }
}
나는 방금 위의 코드를 조금 업데이트했고 그것은 나에게 효과가 있습니다.
언급URL : https://stackoverflow.com/questions/31515776/how-can-i-catch-uniquekey-violation-exceptions-with-ef6-and-sql-server
'programing' 카테고리의 다른 글
파일 크기를 바이트 단위로 반환하는 VBA Excel 기능 (0) | 2023.06.27 |
---|---|
MongoDB 컬렉션 내에 포함된 특정 문서를 가져오는 방법은 무엇입니까? (0) | 2023.06.27 |
당기기 위한 깃 후크가 있습니까? (0) | 2023.06.27 |
트리거 본문에서 동일한 테이블에 대한 다른 삭제를 포함하는 삭제 후 트리거를 작성하는 방법은 무엇입니까? (0) | 2023.06.27 |
반환 매개 변수를 사용하여 최대 절전 모드에서 오라클 함수를 호출하는 방법은 무엇입니까? (0) | 2023.06.27 |