Уловка и обработка 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
). Казалось бы, это лучший способ определить, какая ошибка произошло. Единственная проблема заключается в том, что возвращаемый номер ошибки может отличаться для каждого поставщика базы данных. Если вы планируете переключить поставщиков, вы вернетесь к обработке его так, как вы есть, или создадите слой абстракции, который преобразует эту информацию для вас.
вот пример парня, который использовал код ошибки и файл конфигурации для перевода и локализации удобной ошибки сообщения:
для тех из вас, новичков, кто может выдать ошибку SQL при подключении к БД с другой машины(например, при загрузке формы), вы обнаружите, что при первой установке datatable в C#, который указывает на базу данных SQL server, что он установит такое соединение:
this.Table_nameTableAdapter.Fill(this.DatabaseNameDataSet.Table_name);
возможно, Вам потребуется удалить эту строку и заменить ее чем-то другим, например традиционной строкой подключения, как указано в MSDN и т. д.
вы можете оценить на основе по типу тяжести. Обратите внимание, чтобы использовать это, вы должны быть подписаны на 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: мой запрос на добавление элемента в БД находится в другом проекте (слое)