【问题标题】:A single method throws same type of exception for different reasons in C#在 C# 中,单个方法出于不同原因引发相同类型的异常
【发布时间】:2022-01-24 17:16:03
【问题描述】:

我有一个方法UpdateSQL(),它可能由于两个或多个不同的不相关原因引发相同类型的异常 (SqlException)。原因 1 = 执行 sqlConn.open() 时出现“无效的连接字符串”。原因 2 = 执行 sqlCommand.ExecuteNonQuery() 时出现“执行存储过程时出错”。如何确定在调用方方法中引发 SqlException 的原因,以便我可以记录自定义原因?

调用方法

    try
    {
      UpdateSQL();
    }
    catch(SqlException e)
    {
      // How do i know the reason for which exception was thrown so I could log
      log.LogError(e, "Reason");
    }

更新方法-

    UpdateSQL()
    {
      using (var sqlConn = new SqlConnection("myConnString"))
           {
             sqlConn.Open(); // May throw exception for reason 1
             SqlCommand sqlCommand = new SqlCommand("myStoredProcedure", sqlConn);
             sqlCommand.CommandType = CommandType.StoredProcedure;
        
             // Some random parameter
             SqlParameter myParam = sqlCommand.Parameters.AddWithValue("@Time", "3/10/2015 2:15:10 AM");
             myParam.SqlDbType = SqlDbType.NVarChar;
          
             sqlCommand.ExecuteNonQuery(); // May throw exception for reason 2
           }
    }

我可以看到的一种可能方法是,在 Update 方法中将两个 Sql 命令本身包装在单独的 try-catch 块中。但是有没有办法避免这种情况?

【问题讨论】:

  • 我认为正是出于这个原因,通常会有只抛出专有异常的策略(即,将捕获的所有内容包装起来,然后作为自己的东西重新抛出)。这样您就可以封装供应商依赖项。也就是说,每个 sql 异常中都有信息:状态、消息和供应商特定的错误代码,它们可以帮助您找出要做什么。此外,SqlExceptions 是(或可以是)链式的,也就是说,geNextException() 可能返回非 null,可能指向底层或结果错误。
  • 以编程方式检查 e.Message 在任何情况下通常都不是一个好方法。 ErrorCode 似乎是一个值得探索的好主意。
  • ..您可能最好记录并呈现给人类,而不是试图过多地参与处理它们和修复代码。综上所述,同意其他人的观点,对于 SQLExceptions,每个都有一个数字代码来描述错误的种类,你可以做一些事情来指导你的用户
  • 这可能会有所帮助。 IMO 太复杂了,但是有一些数字和内部异常可以提供更具体的信息。 stackoverflow.com/questions/18894345/…
  • 判断异常是打开连接还是执行查询的方法是查看堆栈跟踪,而不是在日志中编写自定义消息来描述可能抛出的每个操作,以便您知道如何找到引发异常的代码行。这正是堆栈跟踪向您展示的内容。

标签: c# exception


【解决方案1】:

尤其是在日志记录方面,我会避免在 SqlException 周围执行任何自定义逻辑,而只是亲自记录错误消息 + Error number 以进行查找。不这样做的原因是因为日志记录应该相对简单,您不应该编写必须进一步测试的逻辑。

通常只记录类似于docs 共享的内容。这是我通常会在日志中使用的更精简的版本。

Logger.Log(String.Join(Environment.NewLine,
   exception.Errors.Select( (error, i) => $"Error [{i}]: {Error.Message} : Error Number {error.Number}" ));

如果您需要执行实际的业务逻辑,除了非常明确并使用错误编号直接根据错误编号或错误编号范围分支到不同的逻辑之外,没有真正的好方法来处理这个问题。这样的事情应该可以很好地完成这项工作。

try
{
   // do Sql stuff here
}
catch(SqlException sqlException) when (e.Errors.Select(e => e.Number).Contains(new []{25})
{
   // Log Something about bad connection string.. 
   // it happens to be 25 from what I've seen 
   // which tbh I cant find the docs for :(
}
catch(SqlException sqlException) when (e.Errors.Select(e => e.Number).Contains(new []{1,2,3})
{
   // Log Some other kind of Error
}

【讨论】:

  • 是的,这是一种可行的方法,但现在我意识到可能导致“无效连接字符串”的潜在原因太多(例如,某些数字是 18456、11001)正如您自己提到的那样,使这种方法成为一个糟糕的选择。感谢您的提示。学到了很多!
  • 您能否在答案的开头添加一条注释,说明建议在这种情况下避免自定义消息日志记录,因为它会导致不必要的复杂性,但对于信息,可以探索以下解决方案。这将有助于其他阅读答案的人在一开始就知道最佳方法。非常感谢!
  • @swapyy28 感谢您的反馈,我做了一些更改,希望这能更好地阐明我试图提出的观点
猜你喜欢
  • 1970-01-01
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-12
  • 1970-01-01
  • 2012-04-11
  • 2023-03-25
相关资源
最近更新 更多