【问题标题】:where to put try/catch in 3 tier architecture在 3 层架构中放置 try/catch 的位置
【发布时间】:2013-02-06 13:22:37
【问题描述】:

我有一个基于 3 层的 Web 应用程序。我想使用我在业务逻辑层中使用 try catch 块。在业务逻辑中使用 try/catch 块是否正确,或者我需要在我的 UI 层中使用它?

查看我的 DAL 代码。

Data Access Layer

#region Insert in to Logbook
public int Insert_LogBook(string Vehicle_Number, DateTime Vehicle_Booking_Date, TimeSpan Time_From, TimeSpan Time_To, int KM_Start, int KM_End, string Vehicle_Used_By, string Cost_Code, string Budget_Line, DateTime Entry_Date)
{
    try
    {
        SqlCommand com = new SqlCommand("Insert_LogBook", con);
        com.Parameters.Add("@Vehicle_Number", SqlDbType.NVarChar, 100).Value = Vehicle_Number;
        com.Parameters.Add("@Vehicle_Booking_Date", SqlDbType.DateTime).Value = Vehicle_Booking_Date;
        com.Parameters.Add("@Time_From", SqlDbType.Time).Value = Time_From;
        com.Parameters.Add("@Time_To", SqlDbType.Time).Value = Time_To;
        com.Parameters.Add("@KM_Start", SqlDbType.Int).Value = KM_Start;
        com.Parameters.Add("@KM_End", SqlDbType.Int).Value = KM_End;
        com.Parameters.Add("@Vehicle_Used_Byr", SqlDbType.VarChar, 100).Value = Vehicle_Used_By;
        com.Parameters.Add("@Cost_Code", SqlDbType.NVarChar, 50).Value = Cost_Code;
        com.Parameters.Add("@Budget_Line", SqlDbType.NVarChar, 50).Value = Budget_Line;
        com.Parameters.Add("@Entry_Date", SqlDbType.DateTime).Value = Entry_Date;
        con.Open();
        int res = com.ExecuteNonQuery();

    }
    catch (Exception ex)
    {
        WebMsgBox.Show(ex.Message);
    }
    finally
    {
        con.Close();
        con.Dispose();

    }
    return 1;
}
#endregion

所以我应该在我的 bal 或 UI 层中使用它还是我的代码没问题。因为如果我不在 UI 层中使用 try/catch,它不会捕获异常(如果有的话)并显示错误页面。

【问题讨论】:

    标签: c# try-catch 3-tier


    【解决方案1】:

    异常处理和抛出是我一直认为我合作过的大多数开发人员一直误解的东西。

    • 异常允许您发现代码中的错误。
    • 他们停止正在运行的程序以防止对业务造成“伤害”。
    • 它们允许您在合理预期的异常(移动应用上的网络不可用)与 NullReferenceException 等意外错误异常之间进行过滤。
    • 他们直截了当地了解发生错误的原因。
    • 它们允许每个组件添加一层有关上下文和状态的信息以帮助调试,这就是 catch、wrap 和 throw 模式。

    您几乎可以并且应该在任何地方使用 try/catch/(finally),但是...

    只有在知道哪些异常可能发生并且可以从中恢复时才使用 catch。您应该很少捕获基本异常类型。允许所有其他错误冒出并被程序员/测试人员/用户发现。

    如果您要抛出另一个异常并将原始异常附加为 InnerException,则可能需要捕获基本异常类型。

    您不应该三思而后行,也不要懒于创建自己的异常类型。例如,您可能想要编写一个 DataAccessException 并抛出该异常,并在附加在 InnerException 的层中捕获异常。这样,您的日志记录将记录一个异常类型,该类型可以更准确地显示错误发生的位置,并且调用代码可以选择仅捕获 DataAccessException 并执行重试或其他操作。

    您还可以考虑将 DataAccessException 抽象化并将更具体的异常子类化,例如 SqlDataAccessException 或 SecurityDataAccessException。

    您似乎知道,当您想确保某些代码在发生错误时运行时,您使用 finally,即使您不捕获和处理异常本身。在必须始终释放资源的情况下,try/finally 成为标准模式。

    此外,如果可能,请将 try/catch 放在您能够处理错误的最具体的代码补丁周围,从而允许周围代码中的编码错误导致应用程序崩溃。

    您可能会想,“如果我还不知道会抛出哪些异常,我该如何捕获特定异常?”您应该看到它们记录在 ExecuteNonSql 方法中,这就是为什么记录您自己的 API/组件及其引发的异常如此重要的原因。使用 XML cmets 执行此操作,如果提供公共 DLL,请打开 XML 注释文件生成器。

    这似乎需要接受很多,但实际上并非如此。当您投资于日志记录和适当的异常处理/抛出时,您将能够在几分钟内解决错误,您会感觉自己是冠军,并且您很快就会学会对其他人的糟糕代码感到不满:)

    在您编程生涯的这个阶段,我强烈建议您阅读 Cwalina 和 Abrams 的框架设计指南。它将帮助您针对所有这些类型的问题快速做出正确的选择,并且您会发现使用自己的代码就像使用 Microsoft 的 API(大部分情况下)一样令人愉快。

    卢克

    添加一些关于消息的内容。我在错误消息中使用了这种东西。

    “无法{执行某些功能}。发生了{异常类型}。{提供补救建议或错误的常见原因}。请参阅{内部异常|更多日志条目}。”

    例如,在应用中自动保存状态的组件中:

    ...
    catch(FileNotFoundException fnfe)
    {
        string m = String.Format("Cannot save changes. A FileNotFoundException occurred. Check the path '{0}' is valid, that your network is up, and any removable media is available. Please see inner exception.", path);
    
        _log.Error(m, fnfe);
    
        throw new StorageLifecycleException(m, fnfe);
    }
    

    【讨论】:

      【解决方案2】:

      这取决于,您可以在两个层中使用try catch 块。

      但问题不在于您在哪里使用异常处理代码;问题是你如何使用。在您提供的示例中,您正在捕获一个通用的exception,您不知道它是 SqlException 还是任何其他异常。

      一般来说,

      1. 仅捕获您可以处理的异常(在示例中捕获 SqlException,而不是所有异常)

      2. 显示用户友好的消息(在您的示例中,仅显示错误消息对用户没有意义)

      3. 记录异常

      4. 处理发生的异常;如果是DAL相关的异常在DAL层处理,如果是UI相关的异常在UI层处理。

      【讨论】:

      • 你能用我的代码详细说明一下吗?我没有在 3 层使用
      • +1 关于错误消息。我们都是在书呆子信息中长大的,所以我们认为这些信息听起来一定很书呆子。不。他们听起来应该像朋友一样简单地解释问题,甚至提供可能的解决方案。
      【解决方案3】:

      您应该编写一个 try-catch 块来处理异常。没有“总是把try-catch放在这里或那里”这样的事情。 我看到你这样处理异常:

      catch (Exception ex)
      {
          WebMsgBox.Show(ex.Message);
      }
      

      这很糟糕,有几个原因:

      1. 你捕获了通用的Exception 类型。前几天看到一个问题:https://stackoverflow.com/a/14727026/238682
      2. 您尝试在数据访问层中使用 WebMsgBox.Show 处理异常,这打破了层的边界。

      该示例的另一个问题是一个小问题,但我认为从长远来看它很重要(整体代码设计)。 您应该将错误处理逻辑与实际应用程序逻辑分开。因此,当您使用 try-catch 块时,请尽量减少其中的逻辑,这样您的代码就会变得更具可读性。

      public int Insert_LogBook(string Vehicle_Number, DateTime Vehicle_Booking_Date, TimeSpan Time_From, TimeSpan Time_To, int KM_Start, int KM_End, string Vehicle_Used_By, string Cost_Code, string Budget_Line, DateTime Entry_Date)
      {
          using(SqlCommand com = new SqlCommand("Insert_LogBook", con))
          {
              com.Parameters.Add("@Vehicle_Number", SqlDbType.NVarChar, 100).Value = Vehicle_Number;
              com.Parameters.Add("@Vehicle_Booking_Date", SqlDbType.DateTime).Value = Vehicle_Booking_Date;
              com.Parameters.Add("@Time_From", SqlDbType.Time).Value = Time_From;
              com.Parameters.Add("@Time_To", SqlDbType.Time).Value = Time_To;
              com.Parameters.Add("@KM_Start", SqlDbType.Int).Value = KM_Start;
              com.Parameters.Add("@KM_End", SqlDbType.Int).Value = KM_End;
              com.Parameters.Add("@Vehicle_Used_Byr", SqlDbType.VarChar, 100).Value = Vehicle_Used_By;
              com.Parameters.Add("@Cost_Code", SqlDbType.NVarChar, 50).Value = Cost_Code;
              com.Parameters.Add("@Budget_Line", SqlDbType.NVarChar, 50).Value = Budget_Line;
              com.Parameters.Add("@Entry_Date", SqlDbType.DateTime).Value = Entry_Date;
              con.Open();
              int res = com.ExecuteNonQuery();
      
              return 1;
          }
      
      }
      
      public void SomeMethodWhichUsesThatInsert()
      {
          try
          {
              //call Insert_LogBook
          }
          catch(SomeException e)
          {
              //handle
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        您应该将数据访问和业务逻辑放入类库中。 您不应该从 BLL 或 DAL 调用前端组件。 在您的 BLL 和 DAL 日志中创建日志记录,使用类似 log4net 的方式将错误数据放在这里。

        如果要通知用户错误状态,则向上抛出异常。

        【讨论】:

          猜你喜欢
          • 2014-06-12
          • 2018-06-28
          • 1970-01-01
          • 1970-01-01
          • 2014-03-02
          • 2022-01-13
          • 1970-01-01
          • 2011-03-12
          • 2016-04-14
          相关资源
          最近更新 更多