C# 如何捕获特定的SqlException错误?

C# 如何捕获特定的SqlException错误?,c#,exception,sqlexception,C#,Exception,Sqlexception,Q:有没有更好的方法来处理SQLException 以下示例依赖于对信息中文本的解释 Eg1:如果表不存在,我有一个现有的try-catch要处理。 忽略一个事实,我可以首先检查表是否存在 Eg2:没有显示重复密钥异常的try-catch if (sqlEx.Message.StartsWith("Cannot insert duplicate key row in object")) 解决方案:我的SqlExceptionHelper的开始 //-- to see list of error

Q:有没有更好的方法来处理SQLException

以下示例依赖于对信息中文本的解释

Eg1:如果表不存在,我有一个现有的try-catch要处理。
忽略一个事实,我可以首先检查表是否存在

Eg2:没有显示重复密钥异常的try-catch

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

如果您正在寻找更好的方法来处理SQLException,那么您可以做以下几件事。首先,Spring.NET做了一些类似于您正在寻找的东西(我认为)。以下是他们所做工作的链接:

此外,您可以检查错误代码(
sqlEx.Number
),而不是查看消息。这似乎是一种更好的方法来确定发生了哪些错误。唯一的问题是,对于每个数据库提供程序,返回的错误号可能不同。如果您计划切换提供程序,那么您将回到原来的处理方式,或者创建一个抽象层来为您转换这些信息

下面是一个使用错误代码和配置文件翻译和本地化用户友好错误消息的示例:


最好使用错误代码,不必解析

try
{
}
catch (SqlException exception)
{
    if (exception.Number == 208)
    {

    }
    else
        throw;
}
如何确定应使用208:

select message_id
from sys.messages
where text like 'Invalid object name%'
SqlException有一个可以检查的异常。对于重复错误,编号为2601

catch (SqlException e)
{
   switch (e.Number)
   {
      case 2601:
         // Do something.
         break;
      default:
         throw;
   }
 }
要从服务器获取所有SQL错误的列表,请尝试以下操作:

 SELECT * FROM sysmessages
更新

这一点现在可以在C#6.0中简化

有点,有点。看


但问题是,一个好的DAL层会让我们在T-SQL(存储过程)中尝试/捕获
,模式如下。唉,T-SQL
TRY/CATCH
块不能产生原始错误代码,必须产生新的错误,代码超过50000。这使得客户端处理成为一个问题。在SQL Server的下一个版本中,有一个新的构造,允许从T-SQL捕获块重新引发原始异常。

如果希望在SQL Server中遇到错误消息列表,可以使用查看

SELECT *
FROM master.dbo.sysmessages

对于那些从另一台机器(例如,在表单加载时)连接到DB时可能抛出SQL错误的新手,您会发现,当您首次在C#中设置一个指向SQL server数据库的datatable时,它将设置如下连接:

this.Table_nameTableAdapter.Fill(this.DatabaseNameDataSet.Table_name);
您可能需要删除这一行,并将其替换为其他内容,如MSDN中提到的传统连接字符串等


使用MS SQL 2008,我们可以在sys.messages表中列出支持的错误消息

SELECT * FROM sys.messages

您可以根据严重性类型评估。 注意:要使用此功能,您必须订阅OnInfoMessage

conn.InfoMessage += OnInfoMessage;
conn.FireInfoMessageEventOnUserErrors = true;
然后,您的OnInfo消息将包含:

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)
{
包括在内的退货

?lower我正在使用代码优先、C#7和实体框架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:我对数据库中添加项的查询位于另一个项目(层)中

谢谢您的建议。我将创建一个SqlException帮助程序。另外,非常感谢您为我指出sql错误表。选择*from sys.messages where language_id=1033选择*from master.dbo.sysmessages where msglangid=1033或选择*from sys.messages where language_id=1033只过滤英文消息。此外,me系统表中的消息id不是SqlException.Number的一对一映射。我已经在C#和SQL Server 2008 R2上验证了超时错误,SqlException.Number是-2,但在系统表中没有这样的错误定义。如何获取语言id?感谢您的建议和链接。顺便说一句:我喜欢捕捉性别:)我将开始使用它而不是sqlEx有趣的是。这让我想起了旧的经典asp时代
On Error Goto Hell
这听起来不太对劲,在sex中出错。Errors;)Inside不会处理超时和传输异常,因此即使使用存储过程进行完美处理,它仍然适合调用句柄。您可以通过以下语言进行筛选:从msglagid所在的sysmessages中选择*=1033.这是针对EnglishHi@Richard Schneider的,我知道这个答案已经过时了,但我想知道是否有新的(更好的)解决方案也可以自动创建消息,因为我使用MVC,每个模型属性都有自己的“翻译”,我假设我可以用属性名编写标准消息(因为每次映射时它们都是完全相同的)然后获得重命名的属性名,以显示这样的消息:“在我们的系统中已经有一条记录,已知[Value]为[FieldName],请选择不同的记录。”
是否捕获(异常e)
捕获SQL异常?还是必须使用
catch(SqlException e)
?因为某种程度上
捕获(异常e)
没有捕获“SqlException:无法插入重复的键行”.@JedatKinports我们需要查看您的代码,但常规异常会捕获SQL异常,因为后者继承自前者。例如,常规异常上没有数字、服务器、状态或客户端连接id属性。如果您希望从SqlException获得特定的详细信息,则需要显式捕获它。通常您将多个捕获链接在一起,从最具体的捕获开始,以一般异常捕获结束。C#6.0使用archive.org链接修复损坏的链接。我在查找包含DbUpdateException的命名空间时遇到问题。我使用的是Entity Framework 5.0.0.0。但是当我尝试在cat中声明异常类型时DbUpdateException的ch语句,它显示我缺少一个程序集或引用
conn.InfoMessage += OnInfoMessage;
conn.FireInfoMessageEventOnUserErrors = true;
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;
}
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;
    }
}