Исключение дублирующегося ключа отчетности T-SQL для пользователя простым способом - PullRequest
2 голосов
/ 17 января 2010

У меня есть простая таблица стран, использующая столбец идентификаторов для первичного ключа. Существуют также столбцы, содержащие двухбуквенные и трехбуквенные коды стран ISO-3166. Каждый из этих столбцов определяется как уникальный индекс.

При вставках / обновлениях я хочу просто уведомить пользователя, если введенный код ISO уже используется. Я полагаюсь на исключение базы данных для запуска процесса. Я не хочу перегружать пользователей техническими подробностями. Я просто хочу сказать ему, что введенное значение ISO не может быть использовано.

Вот T-SQL в сохраненном обновлении Proc, который я написал. Это кажется длинным и склонным к будущим ошибкам и / или постоянному обслуживанию, поскольку приложение растет и все меняется. Есть ли лучший или более простой способ? Я мог бы быть немного мокрым за ушами с .net, но я думал, что это будет легче.

BEGIN TRY
  UPDATE [Country] SET [CountryName] = @CountryName, [CountryISO] = @CountryISO, [CountryISO3] = @CountryISO3, [UpdateDate] = @updateDate WHERE (([CountryID] = @CountryID) AND ([RowVersion] = @Original_RowVersion));
END TRY

BEGIN CATCH
  DECLARE @ErrSeverity int, @ErrNumber int, @ErrLine int
  DECLARE @ErrMsg nvarchar(4000)
  SELECT @ErrSeverity = ERROR_SEVERITY(), @ErrNumber = ERROR_NUMBER(),@ErrState = ERROR_STATE(),
         @ErrMsg = 
            CASE WHEN ERROR_NUMBER() = 2601
            THEN 
              CASE 
              WHEN ISNULL(CHARINDEX('IX_COUNTRYISO3', ERROR_MESSAGE()), 0) > 0
                THEN 'The 3 letter ISO-3166 value entered is already in use. Please enter a unique 3 letter ISO-3166 value.'
              WHEN ISNULL(CHARINDEX('IX_COUNTRYISO', ERROR_MESSAGE()), 0) > 0
                THEN 'The 2 letter ISO-3166 value entered is already in use. Please enter a unique 2 letter ISO-3166 value.'
              ELSE 
                ERROR_MESSAGE() + '(SQL ErrNo: ' + CONVERT(varchar(50), ERROR_NUMBER()) + ')' 
              END 
            ELSE 
              ERROR_MESSAGE() + '(SQL ErrNo: ' + CONVERT(varchar(50), ERROR_NUMBER()) + ')'
            END;
  RAISERROR(@ErrMsg, @ErrSeverity, @ErrState)
END CATCH

Является ли мое решение жизнеспособным?
Каков наилучший способ поделиться этим кодом исключения между StoredProcs для вставки и обновления?

Полагаю, я прошу пересмотреть код.

Большое спасибо, Майк

Ответы [ 2 ]

1 голос
/ 17 января 2010

Согласен, такой код станет головной болью при обслуживании. В самом коде нет ничего плохого, просто проблема возникает слишком поздно.

Уникальные ограничения / индексы - это «последняя линия защиты», по крайней мере, в моем мире. Если вы хотите обеспечить хороший пользовательский опыт, вы не можете ждать, пока данные уже передаются в базу данных. Вы должны активно проверять наличие дубликатов на уровне пользовательского интерфейса и предупреждать пользователя о том, что он собирается отправить дубликат записи (и отключать его, если дублирующиеся ключи не разрешены).

Если дублирующий ключ в конечном итоге отправляется, что, по сути, описывает ошибку в логике приложения, поэтому вполне допустимо, чтобы ваша база данных выдавала сообщение об ошибке типа «база данных», вам не нужно пытаться Укрась это. Приложение должно стараться обрабатывать такие ошибки как можно более изящно, как с любым другим непредвиденным исключением.

Если вы просто спрашиваете, существует ли способ модульной функциональности такого рода в T-SQL ... Я так не думаю. Во всяком случае, без безобразного беспорядка динамического SQL.

Обновление - только что увидел комментарий к другому ответу, если вы хотите вернуть номер ошибки SQL в C #, тогда все достаточно просто:

const int SqlDuplicateKeyError = 2601;

try
{
    db.Update(record);
}
catch (SqlException ex)
{
    switch (ex.Number)
    {
        case SqlDuplicateKeyError:
            // Custom error handling here
        default:
            throw;
    }
}
0 голосов
/ 17 января 2010

Лично я мог бы представить, что просто дать вашим уникальным ограничениям / индексам разумные и подробные имена было бы лучшим вариантом, чем это.

Если пользователи взаимодействуют с базой данных напрямую, им следует сдавать в аренду знания в области ИТ.

Если пользователи взаимодействуют через пользовательский интерфейс, вам следует реализовать обработку ошибок в интерфейсе пользователя.

Чтобы поймать его во внешнем интерфейсе, посмотрите эту ссылку , базовый пример (и, вероятно, ошибочный) ...

Catch (SqlExpcetion ex)
{
    if (e.Number == 2061 && ex.Message.Contains("IX_COUNTRYISO3")
    {
        MessageBox.Show("The 3 letter ISO-3166 value entered is already in use. Please enter a unique 3 letter ISO-3166 value.");
    }
    else
    {
         MessageBox.Show(String.Format("Error ({0}):{1}",ex.Number,ex.Message)
    }
}
Catch (Exception ex)
{
    MessageBox.Show(ex);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...