문제

I have a 3 tier based web application. I want to use I am using try catch block in my business logic layer. Is it right to use try/catch block in business logic or I need to use it in my UI layer?

see my code for 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

So should I use it in my bal OR IN UI layer or my code is ok. because If I don't use try/catch in my UI layer it won't catch exception (if there is any) and shows the error page.

도움이 되었습니까?

해결책

Exception handling and throwing is something which I see misunderstood all the time, by most developers I've worked with.

  • Exceptions allow you to find bugs in your code.
  • They halt the running program to prevent 'injury' to the business.
  • They allow you to filter between reasonably expected exceptions (network unavailable on a mobile app) versus unexpected bug exceptions like NullReferenceException.
  • They get straight to the point about why an error occurred.
  • They allow each component to add a layer of information about context and state to help debugging, this is the catch, wrap and throw pattern.

You can and should use try/catch/(finally) pretty much anywhere, but...

You use catch only when you know which exception(s) are likely to occur and can recover from them. You should rarely ever catch the base Exception type. Allow all other errors to bubble up and be found by a programmer/tester/user.

You may want to catch a base exception type if you are going to throw another exception and attach the original exception as the InnerException.

You shouldn't think twice or get lazy about about creating your own exception types. For example, you may want to write a DataAccessException and throw that with exceptions caught within the tier attached in the InnerException. This way your logging will log an exception type that shows more accurately where the error occurred and calling code can choose only to catch the DataAccessException and perform a retry or something.

You could also consider making DataAccessException abstract and subclassing more specific exceptions like SqlDataAccessException or SecurityDataAccessException.

As you appear to know, you use finally when you want to ensure some code is run in the event of an error, even if you don't catch and handle the exception itself. In situations where you must always release a resource, the try/finally becomes a standard pattern.

Also, when possible, put the try/catch around the most specific patch of code where you are able to handle the error, allowing coding bugs in the surrounding code to crash the app.

You may think, "How can I catch a specific exception if I don't yet know which exceptions will be thrown?" You should see them documented on the ExecuteNonSql method and this is why it is so important to document your own API/component with the exceptions that it throws. Use XML comments to do this and if shipping a public DLL, switch on the XML comment file generator.

This may seem like a lot to take in, but its not in practice. When you invest in logging and proper exception handling/throwing, you'll be able to resolve bugs in minutes, you'll feel like champ and you'll soon learn to get upset with everyone else's poor code :)

At this stage in your programming life, I strongly recommend reading Framework Design Guidelines by Cwalina and Abrams. It will help you make the right choices quickly about all these types of question and you'll find using your own code is as joyful as using Microsoft's APIs (mostly).

Luke

Adding a bit about messages. I use this sort of thing in the error message.

"Cannot {perform some function}. A {type of exception} occurred. {Offer remedial advice or common reasons for the error}. Please see {inner exception|further log entries}."

For example, in a component for auto saving state in an app:

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

다른 팁

It just depends, you may use try catch blocks in both layers.

But the problem is not where you are using exception handling code; the problem is how you are using. In the example you have provided you are catching a generic exception, you do not know whether it is SqlException, or any other exception.

In general,

  1. catch only the exceptions you can handle (in the example catch SqlException, not all the exceptions)

  2. display a user friendly message (in your example simply displaying the error message makes no sense for user)

  3. log the exception

  4. handle the exception where it occurs; if it is a DAL related exception handle it in the DAL layer, if it is a UI related exception handle it in the UI layer.

You should write a try-catch block where you can handle the exception. There is no such thing as 'always put the try-catch here or there'. I see you handle the exception like this:

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

This is bad because of several reasons:

  1. You catch the generic Exception type. I saw a question about this a few days ago: https://stackoverflow.com/a/14727026/238682
  2. You try to handle the exception with WebMsgBox.Show within your Data Access Layer, which breaks the boundaries of the layer.

Another problem with the example is a minor problem but I think it is important in the long term (overall code design). You should separate the error handling logic from the actual application logic. So when you use the try-catch block try to minimize the logic inside that, so your code became more readable.

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
    }

}

You should put your data-access and business logic into class libraries. You should not be calling front end components from BLL or DAL. Create logging in your BLL and DAL log these using something like log4net put error data here.

then throw the exception upwards if you want to notify users of error state.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top