T-SQL以简单的方式向用户报告重复键异常

时间:2010-01-17 20:36:56

标签: .net exception-handling

我有一个简单的国家/地区表,使用主键的标识列。还有用于包含2个字母和3个字母ISO-3166国家代码的列。这些列中的每一列都被定义为唯一索引。

On Inserts / Updates我想简单地通知用户输入的ISO代码是否已被使用。我依靠数据库异常来触发进程。我不想用技术细节压倒用户。我只想告诉他输入的ISO值不能使用。

这是我写的更新存储过程中的T-SQL。随着应用程序的增长和变化,它似乎很长并且容易出现未来的错误和/或持续的维护。有更好或更简单的方法吗?我可能会在.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

我的解决方案是否可行? 在Insert和Update StoredProcs之间共享此异常代码的最佳方法是什么?

我想我实际上是在要求进行代码审查。

非常感谢Mike

2 个答案:

答案 0 :(得分:1)

我同意,这样的代码将成为维护头痛的问题。代码本身没有任何问题,只是问题被抓得太晚了。

独特的约束/索引是“最后一道防线”,至少在我的世界里。如果要提供良好的用户体验,则不能等到数据已经提交到数据库。您应该在UI级别主动检查重复项,并警告用户他/她将要提交重复的条目(如果不允许重复的密钥,则禁用提交)。

如果重复的密钥最终被提交,那实际上描述了应用程序逻辑中的错误,因此您的数据库可以吐出“类似数据库”的错误消息,您无需尝试把它搞定。应用程序应尽可能优雅地处理此类错误,与任何其他意外异常一样。

如果您只是在询问是否有办法在T-SQL中模块化这种功能......我不这么认为。无论如何,并非没有动态SQL的混乱。

更新 - 只是看到了对其他答案的评论,如果你想在C#中获取SQL错误号,那么这很简单:

const int SqlDuplicateKeyError = 2601;

try
{
    db.Update(record);
}
catch (SqlException ex)
{
    switch (ex.Number)
    {
        case SqlDuplicateKeyError:
            // Custom error handling here
        default:
            throw;
    }
}

答案 1 :(得分:0)

就我个人而言,我认为简单地给你的不明确的约束/索引明智和冗长的名字将是一个比这更好的选择。

如果用户直接与数据库交互,那么他们应该至少掌握IT知识。

如果用户通过自定义前端进行交互,那么您应该在前端实现错误处理。

要在前端捕获它,请参阅此link,这是一个基本示例(可能还有错误)示例...

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