Nice programing

SqlException catch 및 처리

nicepro 2020. 12. 6. 22:06
반응형

SqlException catch 및 처리


Q : SqlException을 처리하는 더 좋은 방법이 있습니까?

아래 예는 메시지의 텍스트 해석에 의존합니다.

예 1 : 테이블이없는 경우 처리 할 기존 try catch가 있습니다.
테이블이 처음에 존재하는지 확인할 수 있다는 사실을 무시하십시오.

try
{
    //code
}
catch(SqlException sqlEx)
{
        if (sqlEx.Message.StartsWith("Invalid object name"))
        {
            //code
        }
        else
            throw;
}

Eg2 : 중복 키 예외를 보여주는 try catch없이

if (sqlEx.Message.StartsWith("Cannot insert duplicate key row in object"))

솔루션 : 내 SqlExceptionHelper의 시작

//-- to see list of error messages: select * from sys.messages where language_id = 1033 order by message_id
public static class SqlExceptionHelper
{
    //-- rule: Add error messages in numeric order and prefix the number above the method

    //-- 208: Invalid object name '%.*ls'.
    public static bool IsInvalidObjectName(SqlException sex)
    { return (sex.Number == 208); }

    //-- 2601: Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'. The duplicate key value is %ls.
    public static bool IsDuplicateKey(SqlException sex)
    { return (sex.Number == 2601); }
}

SqlException에는 확인할 수 있는 Number 속성 이 있습니다. 중복 오류의 경우 번호는 2601입니다.

catch (SqlException e)
{
   switch (e.Number)
   {
      case 2601:
         // Do something.
         break;
      default:
         throw;
   }
 }

서버에서 모든 SQL 오류 목록을 얻으려면 다음을 시도하십시오.

 SELECT * FROM sysmessages

최신 정보

이제 C # 6.0에서 단순화 할 수 있습니다.

catch (SqlException e) when (e.Number == 2601)
{
   // Do something.
}

일종의, 일종의. 데이터베이스 엔진 오류의 원인 및 해결 방법을 참조하십시오.

class SqllErrorNumbers
{ 
   public const int BadObject = 208;
   public const int DupKey = 2627;
}

try
{
   ...
}
catch(SqlException sex)
{
   foreach(SqlErrorCode err in sex.Errors)
   {
      switch (err.Number)
      {
      case SqlErrorNumber.BadObject:...
      case SqllErrorNumbers.DupKey: ...
      }
   }
}

하지만 문제는 좋은 DAL 계층이 예외 처리 및 중첩 트랜잭션과 같은 패턴을 사용하여 T-SQL (저장 프로 시저) TRY/CATCH 내부에 있다는 입니다. 아아, T-SQL 블록은 원래 오류 코드를 발생시킬 수 없으며 50000 이상의 코드 새로운 오류를 발생 시켜야합니다 . 이로 인해 클라이언트 측에서 문제를 처리하게됩니다. 다음 버전의 SQL Server에는 T-SQL catch 블록에서 원래 예외를 다시 발생시킬 수 있는 새로운 THROW 구문이 있습니다.TRY/CATCH


오류 코드를 사용하는 것이 더 좋으며 구문 분석 할 필요가 없습니다.

try
{
}
catch (SqlException exception)
{
    if (exception.Number == 208)
    {

    }
    else
        throw;
}

208이 사용되어야한다는 것을 알아내는 방법 :

select message_id
from sys.messages
where text like 'Invalid object name%'

SQL 서버에서 만난 오류 메시지 목록을 원하면 다음과 같이 볼 수 있습니다.

SELECT *
FROM master.dbo.sysmessages

MS SQL 2008에서는 지원되는 오류 메시지를 sys.messages 테이블에 나열 할 수 있습니다.

SELECT * FROM sys.messages

If you are looking for a better way to handle SQLException, there are a couple things you could do. First, Spring.NET does something similar to what you are looking for (I think). Here is a link to what they are doing:

http://springframework.net/docs/1.2.0/reference/html/dao.html

Also, instead of looking at the message, you could check the error code (sqlEx.Number). That would seem to be a better way of identifying which error occurred. The only problem is that the error number returned might be different for each database provider. If you plan to switch providers, you will be back to handling it the way you are or creating an abstraction layer that translates this information for you.

Here is an example of a guy who used the error code and a config file to translate and localize user-friendly error messages:

https://web.archive.org/web/20130731181042/http://weblogs.asp.net/guys/archive/2005/05/20/408142.aspx


For those of you rookies out there who may throw a SQL error when connecting to the DB from another machine(For example, at form load), you will find that when you first setup a datatable in C# which points to a SQL server database that it will setup a connection like this:

this.Table_nameTableAdapter.Fill(this.DatabaseNameDataSet.Table_name);

You may need to remove this line and replace it with something else like a traditional connection string as mentioned on MSDN, etc.

http://www.connectionstrings.com/sql-server-2008


You can evaluate based on severity type. Note to use this you must be subscribed to OnInfoMessage

conn.InfoMessage += OnInfoMessage;
conn.FireInfoMessageEventOnUserErrors = true;

Then your OnInfoMessage would contain:

foreach(SqlError err in e.Errors) {
//Informational Errors
if (Between(Convert.ToInt16(err.Class), 0, 10, true)) {
    logger.Info(err.Message);
//Errors users can correct.
} else if (Between(Convert.ToInt16(err.Class), 11, 16, true)) {
    logger.Error(err.Message);
//Errors SysAdmin can correct.
} else if (Between(Convert.ToInt16(err.Class), 17, 19, true)) {
    logger.Error(err.Message);
//Fatal Errors 20+
} else {
    logger.Fatal(err.Message);
}}

This way you can evaluate on severity rather than on error number and be more effective. You can find more information on severity here.

private static bool Between( int num, int lower, int upper, bool inclusive = false )
{
    return inclusive
        ? lower <= num && num <= upper
        : lower < num && num < upper;
}

I am working with code first, C# 7 and entity framework 6.0.0.0. it works for me

Add()
{
     bool isDuplicate = false;
     try
     {
       //add to database 
     }
     catch (DbUpdateException ex)
     {
       if (dbUpdateException.InnerException != null)
       {
          var sqlException = dbUpdateException.InnerException.InnerException as SqlException;
          if(sqlException == null)
             isDuplicate = IsDuplicate(sqlException);
       } 
     }
     catch (SqlException ex)
     {
        isDuplicate = IsDuplicate(ex);
     }  
     if(isDuplicate){
       //handle here
     }
}

bool IsDuplicate(SqlException sqlException)
{
    switch (sqlException.Number)
    {
        case 2627:
            return true;
        default:
            return false;
    }
}

N.B: my query for add item to db is in another project(layer)

참고URL : https://stackoverflow.com/questions/6221951/sqlexception-catch-and-handling

반응형