Захват конкретного исключения

Как я могу поймать конкретное исключение с помощью С#?
В моей базе данных есть уникальный индекс для некоторых столбцов.
когда пользователь вставляет двойную запись, это исключение было выбрано:

Невозможно вставить повторяющуюся строку ключа в объект 'dbo.BillIdentity' с уникальным index 'IX_BillIdentity'. утверждение завершено.

Как я могу поймать это исключение?
В настоящее время я проверяю этот код:

 catch (Exception ex) {
    if (ex.Message.Contains("Cannot insert duplicate key row in object 'dbo._BillIdentity' with unique index 'IX__BillIdentity")) {
        string ScriptKey = "$(function() {ShowMessage('Error');});";
        ScriptManager.RegisterStartupScript(Page, GetType(), "script", ScriptKey, true);
    }
}

Я думаю, что его плохой код запаха.
Есть ли лучший способ?

Ответ 1

Вы не указали тип генерируемого исключения, но вы можете поймать этот конкретный тип исключения. Например:

catch (DuplicateKeyException e) {
    ...
}

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

Обратите внимание, что если вы действительно не обрабатываете исключение, вы должны, вероятно, изменить его:

catch (SqlException e) {
    if (CheckWeCanHandle(e)) {
        // Mess with the ScriptManager or whatever
    } else {
        throw;
    }
}

Ответ 2

Обрабатывать SqlException только в этом случае.

[изменить]

Чтобы проверить дублирующее ключевое исключение на сервере MS SQL:

try
{
    // try to insert
}
catch (SqlException exception)
{
    if (exception.Number == 2601) // Cannot insert duplicate key row in object error
    {
        // handle duplicate key error
        return;                  
    }
    else
        throw; // throw exception if this exception is unexpected
}

Изменить: Откуда берутся 2601?

select *
from sys.messages
where text like 'Cannot insert duplicate key%'

Возврат:

message_id  language_id severity is_event_logged text
----------- ----------- -------- --------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2601        1033        14       0               Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'. The duplicate key value is %ls.

Используя exception.Number и ссылаясь на представление sys.messages, вы можете обрабатывать любое конкретное исключение MS SQL.

Ответ 3

Я только что выбрал проект, где кто-то пошел по этому маршруту:

Catch ex As SqlException
    Select Case ex.Number
            Case 2601
                ...

Обратите внимание на следующее (из sys.messages в SQL Server):

2601 - Невозможно вставить повторяющуюся строку ключа в объект "%. * ls" с уникальным индексом "%. * ls".

Но как насчет этого..?

2627 - Нарушение ограничения% ls '%. * ls'. Невозможно вставить дублирующий ключ в объект "%. * Ls".

Я просто потратил некоторое время на поиск именно этой проблемы.

А что, если мы изменим поставщика БД? Предположительно 2601 не является абсолютно универсальным... Это воняет, ИМО. И если вы имеете дело с этим в своем слое презентации, я думаю, что есть большие вопросы, которые нужно задать.

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

В настоящее время я склоняюсь к выполнению легкого SELECT для ID в открытом соединении и вообще избегаю исключения.

Ответ 4

Вы можете только поймать SqlException для стартеров

catch (SqlException ex) {
    if (ex.Message.Contains("Cannot insert duplicate key row in object 'dbo._BillIdentity' with unique index 'IX__BillIdentity")) {
        string ScriptKey = "$(function() {ShowMessage('Error');});";
        ScriptManager.RegisterStartupScript(Page, GetType(), "script", ScriptKey, true);
    }
}

Ответ 5

Как описано здесь, вы можете использовать фильтры исключений. Пример:

try
{ /* your code here */ }
catch (SqlException sqlex) when (sqlex.Number == 2627)
{ /* handle the exception */ }

Ответ 6

Рабочий код для фильтра только дубликат исключения первичного ключа исключения

using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;

.........

 try{
    abc...
    }
    catch (DbUpdateException ex)
                {
                    if (ex.InnerException.InnerException is SqlException sqlEx && sqlEx.Number == 2601)
                    {
                        return ex.ToString();
                    }
                    else
                    {
                        throw;
                    }
                }