programing

EF6 및 SQL Server에서 UniqueKey Violation 예외를 어떻게 탐지할 수 있습니까?

mytipbox 2023. 6. 27. 23:27
반응형

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와 함께DbContextAPI(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제 생각에 당신은 그것을 사용하고 있는 것 같습니다.ObjectContextAPI, 하지만 비슷해야 합니다.

저의 경우 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

반응형