我有一个简单的国家/地区表,使用主键的标识列。还有用于包含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
答案 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);
}