【问题标题】:Is it bad practice to catch the "happy path" in an exception?在异常中捕获“快乐路径”是不好的做法吗?
【发布时间】:2019-11-01 04:00:31
【问题描述】:

我有一个 SQL 语句来检查一个值是否在我的数据库中。如果数据库中的值是 not,我想用“快乐路径”来响应。 我发现使用 DbDataReader (.NET) 如果 SELECT 查询找不到它会引发异常的值 - 所以我的“快乐路径”最终出现在异常中,而不是在主 try 块中。

我总是可以说“NOT IN”,但我不想返回数据库中没有值的所有行 - 因为这会返回数千个结果,而我想要的只是一个“不,它不在这里”类型的响应。

public void wristbandScan(string barcode)
    {
        string query = "SELECT ticket FROM tickets WHERE 
                             linked_barcode='" + barcode + "'";

        ValidTicketEventArgs args = new ValidTicketEventArgs();

        try
        {
            var queryResult = _dbRunner.queryThis(query);

            args.Result = false;
            args.Message = "WB already linked";
            args.Barcode = barcode; 
            OnValidTicketEvent(args);
        }
        catch (Exception e)
        {
            this.updateWristband(barcode);
            this.updateValid();
            args.Result = true;
            args.Message = "WB linked";
            args.Barcode = barcode;
            OnValidTicketEvent(args);
        }
    }

在错误语句中捕捉快乐路径对我来说是错误的,但我不希望与使用 NOT IN 语句读取所有行相关的延迟。

有没有更好的方法来做到这一点,或者这种方法是可接受的最佳实践?

【问题讨论】:

  • 是的,非常糟糕的做法。例外非常昂贵,应该只是:例外,不是你期望发生的事情,更不用说“快乐”的结果了!
  • 这是我的想法。那么如何让 SELECT 语句在找不到值时不返回异常呢?也许限制是我对 SQL 的了解?
  • 通常情况下,如果 SELECT 查询找不到它会引发异常的值 嗯?在什么情况下是真的??通常,查询返回一个空的结果集。 _dbRunner.queryThis 是什么??
  • 最好从像实体框架这样的 ORM(对象到关系管理器)开始(其他选项可用),此时您可以将表视为对象的集合,而不必担心底层 SQL 代码。它还可以消除您当前代码可能遭受的 SQL 注入攻击。
  • 我不知道为什么SELECT 如果没有找到结果会抛出异常,它应该返回一个空集。但是,如果您只关心条形码的存在,您可以将查询更改为COUNT(1) FROM tickets WHERE linked_barcode=<value>。如果未找到匹配项,则返回 0,否则将返回大于 0 的值

标签: c# sql error-handling try-catch


【解决方案1】:

好吧,您不必获取所有记录给客户端;让我们为此提取一个方法。假设您使用MS Sql

 public bool hasScanCode(string barcode) {
   if (string.IsNullOrWhiteSpace(barcode)) 
     return false;

   //DONE: paramterize queries 
   string query = 
      @"SELECT ticket 
          FROM tickets 
         WHERE linked_barcode = @prm_BarCode";

   using (var conn = new SqlConnection(connection_string_here)) {
     conn.Open();

     using (var q = new SqlCommand(conn, query)) {
       //TODO: q.Parameters.Add is a better choice
       q.Parameters.AddWithValue("@prm_BarCode", barcode.Trim()); 

       using (var reader = q.ExecuteReader()) {
         // we read (fetch) at most 1 record
         // if empty cursor - no record with given barcode 
         return reader.Read(); 
       }
     }
   }
 }

那么我们就可以使用它了:

 public void wristbandScan(string barcode) {
   bool result = hasScanCode(barcode); 

   ValidTicketEventArgs args = new ValidTicketEventArgs() { 
     Result = result,
     Message = result ? "WB linked" : "WB already linked",
     Barcode = barcode, 
   };  

   OnValidTicketEvent(args);       
 }

请记住 - exceptions 适用于例外情况。异常非常(堆栈展开需要资源);它们不可读 - catch,事实上,就像一个臭名昭著的goto;它们是危险的 - 在你当前的代码中你catch 太多的例外:例如AccessViolationException 如果在 dbRunner.queryThis 内的某个地方抛出它,将被有效地屏蔽

【讨论】:

  • 感谢这个解决方案,我重新设计了我的实现,效果很好。
【解决方案2】:

创建并调用一个StoredProcedure,它可以处理空的情况并且不返回任何行而不是异常。

然后处理try/catch之外的无行场景。

【讨论】:

    猜你喜欢
    • 2011-09-26
    • 2011-11-26
    • 1970-01-01
    • 2020-11-10
    • 2019-11-29
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    相关资源
    最近更新 更多