Уловка и обработка SqlException

Q: есть ли лучший способ справиться с SqlExceptions?

приведенные ниже примеры основаны на интерпретации текста в сообщении.

Eg1: у меня есть существующий try catch для обработки, если таблица не существует.
игнорируйте тот факт, что я мог бы проверить, существует ли таблица в первую очередь.

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

Eg2: без попытки поймать, показывая дубликат ключа исключение

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

9 ответов


SqlException имеет номер что вы можете проверить. Повторяющиеся ошибки номер 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.
}

вроде, вроде. См.причина и разрешение ошибок компонента Database Engine

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 будет нам TRY/CATCH внутри T-SQL (хранимые процедуры), с шаблоном типа обработка исключений и вложенные транзакции. Увы, T-SQL TRY/CATCH блок не может поднять исходный код ошибки, придется поднять новая ошибки, с кодом выше 50000. Это делает клиентскую сторону обработки проблемы. В следующей версии SQL Server появляется новая бросить построить, которые позволяют повторно поднять исходное исключение из блоков T-SQL 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 server, вы можете увидеть с

SELECT *
FROM master.dbo.sysmessages

С MS SQL 2008 мы можем перечислить поддерживаемые сообщения об ошибках в таблице sys.сообщения

SELECT * FROM sys.messages

Если вы ищете лучший способ обработки SQLException, есть несколько вещей, которые вы могли бы сделать. Во-первых, Spring.NET делает что-то похожее на то, что вы ищете (я думаю). Вот ссылка на то, что они делают:

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

кроме того, вместо того, чтобы смотреть на сообщение, вы можете проверить код ошибки (sqlEx.Number). Казалось бы, это лучший способ определить, какая ошибка произошло. Единственная проблема заключается в том, что возвращаемый номер ошибки может отличаться для каждого поставщика базы данных. Если вы планируете переключить поставщиков, вы вернетесь к обработке его так, как вы есть, или создадите слой абстракции, который преобразует эту информацию для вас.

вот пример парня, который использовал код ошибки и файл конфигурации для перевода и локализации удобной ошибки сообщения:

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


для тех из вас, новичков, кто может выдать ошибку SQL при подключении к БД с другой машины(например, при загрузке формы), вы обнаружите, что при первой установке datatable в C#, который указывает на базу данных SQL server, что он установит такое соединение:

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

возможно, Вам потребуется удалить эту строку и заменить ее чем-то другим, например традиционной строкой подключения, как указано в MSDN и т. д.

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


вы можете оценить на основе по типу тяжести. Обратите внимание, чтобы использовать это, вы должны быть подписаны на OnInfoMessage

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

тогда ваш OnInfoMessage будет содержать:

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

таким образом, вы можете оценить серьезность, а не номер ошибки и быть более эффективным. Вы можете найти более подробную информацию о тяжести здесь.

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

сначала я работаю с кодом, C# 7 и entity framework 6.0.0.0. это работает для меня

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: мой запрос на добавление элемента в БД находится в другом проекте (слое)