7
\$\begingroup\$

I found the following two types of exception-handling in Business Logic Layer.

ASP.NET 3.5 Enterprise Application Development uses a similar method like the first one (I read it few years ago).

I also found this on Stack Overflow, but it doesn't answer my question.

I'm wondering which one is better design and efficiency.

Method 1 - in Business Logic Layer

private int InsertUser(string firstname, string lastname, ref List<string> errors)
{
    if (!string.IsNullOrEmpty(firstname))
        errors.Add("First name is required.");

    if (!string.IsNullOrEmpty(lastname))
        errors.Add("Last name is required.");

    if (errors.Count > 0)
        return -1;

    int userId = -1;

    try
    {
        // Insert user and return userId    
    }
    catch (Exception ex)
    {
        // Log error to database
        errors.Add("Error occurs. Please contact customer service.");
    }

    return userId;
}

Method 2 - in Business Logic Layer

private int InsertUser(string firstname, string lastname)
{
    if (!string.IsNullOrEmpty(firstname))
        throw new ArgumentNullException(firstname);

    if (!string.IsNullOrEmpty(lastname))
        throw new ArgumentNullException(lastname);

    int userId = -1;

    // Insert user and return userId. Let user handle the exception in UI.

    return userId;
}

The disadvantage of Method 2 is that the UI has to filter out what to display/not display the error depending on the exception. For instance, you might not want to display a System.Data.Entity exception to the user.

\$\endgroup\$
  • 1
    \$\begingroup\$ As an aside, you don't have to pass a List<string> as a ref argument. List<T>, like all classes, is a reference type, so changes made to errors by InsertUser() will always be reflected in the original object (assignment to errors would not be reflected without ref). \$\endgroup\$ – Frédéric Hamidi Jul 29 '11 at 20:32
9
\$\begingroup\$

It can be interesting to convey more information about the error to the end user, but it doesn't mean the UI layer has to be the only one to handle the exception.

You can both log the error into your database from the business layer and relay it to the UI layer if you rethrow the exception:

try {
    // Insert user and return userId...
} catch (Exception ex) {
    // Log error to database...
    throw;  // Relay error to UI layer.
}
\$\endgroup\$
  • \$\begingroup\$ Thank you for the comment. Will there be multiple entries in database if each layer logs the same error to DB? In addition, we do not want to display somethign like System.Data.Entity to user. Therefore, UI have to somehow figure what to display/not display the error. \$\endgroup\$ – Win Jul 29 '11 at 19:29
  • 1
    \$\begingroup\$ @Win, I was under the impression that the UI layer could not log anything to the database, mostly because it was using the business layer as an abstraction instead of hitting the DB directly. Was I wrong? \$\endgroup\$ – Frédéric Hamidi Jul 29 '11 at 19:32
  • \$\begingroup\$ @Frédéric - It is also true. My main concern is that UI have to somehow filter what to display/not display the error. I do not want to display to user something like System.Data.Entity. \$\endgroup\$ – Win Jul 29 '11 at 19:42
  • 4
    \$\begingroup\$ @Win, you do not seem comfortable with the UI layer deciding what to display to the end user. Why? The entire purpose of the UI layer is to query the business layer and format that information in a appropriate manner for the end user. The business layer can indeed do that job and pass, say, prerendered markup to the UI layer, but why would it do so? After all, its entire purpose it to abstract the data source in a UI-independent way. \$\endgroup\$ – Frédéric Hamidi Jul 29 '11 at 19:52
  • \$\begingroup\$ @Frédéric - Good point! I'll definitely keep in mind that. Thank you! \$\endgroup\$ – Win Jul 29 '11 at 19:59
3
\$\begingroup\$

My approach is to catch and log/report errors at the highest point in the call stack possible, unless I have a way of actually handling them (ie I can take steps to allow the app to continue processing)in which case I'll add a try/catch at the appropriate point.

Since it looks like all you are going to do with the error is log it, I'd use Method 2 in this particular instance.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your comment. Now, I lean more towards method 2 - UI handling exceptions. \$\endgroup\$ – Win Jul 29 '11 at 21:36
3
\$\begingroup\$

You should separate two different tasks: Business rules validation and error handling. In general, business rules validations should be processed with Method 1. If you are working on a new project, you even may create a special set of classes for business rules validation. Then your example can be modified like this:

class User
{
    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }
}

static class Validator
{
    public static List<string> Validate(object objectToValidate)
    {
        List<string> result = new List<string>();
        //Get all properties with "Required" attribute
        //For each property
          //If this property is empty
            //Add to result
       //Return result
    }
}

Then, your UI code will be like:

var validationErrors = Validator.Validate(user);
if (validationErrors.Count == 0)
{
   UsersService.Insert(user);         
}
else
{
    private DisplayErrors(validationErrors );
}

And UsersService.Insert code:

var validationErrors = Validator.Validate(user);
if (validationErrors.Count == 0)
{
   Repository.Insert(user);         
}
else
{
    throw new Exception("Some details about validation errors.");
}

Of course, it is a very simplified example and there are hundreds of variants, but the idea is to separate the validation and the error handling.

\$\endgroup\$
  • \$\begingroup\$ Interesting approach! Thank you for your comment \$\endgroup\$ – Win Aug 2 '11 at 16:02
0
\$\begingroup\$

It can be more work for someone using method 1 to catch the errors thrown by null arguments. Also, if they can't see your code then it would be a downright pain in the ass. Other than that, it is your choice. Typically, people just put stuff into a try catch and expect the lib to throw exceptions. I don't recommend having two ways of reporting errors.

\$\endgroup\$
0
\$\begingroup\$

I have heard strong arguments that it should be handled in both, because the business layer needs to ensure the integrity of the data being stored. In particular, if the business layer is being used from multiple front end applications, then it is important that the errors are handled within the buisness layer so that you can be certain of integrity wherever the data comes from.

Additionally, they need to be handled in the UI, becasue the user needs as much information as you can provide ( in an appriate way ) about why data is wrong. It is important that I as the user am told that the data I have entered is wrong and why - it may be invalid data, or duplicate, and I need to know which.

Having said that, I don't really agree with this view. In particular, if you have a single UI working with a buisness layer, I would be tempted to put the validation in the UI, to provide the best feedback to the user about what has gone wrong. This might involve picking up exceptions from the business layer.

\$\endgroup\$
0
\$\begingroup\$

Event though try/catch is slower I would prefer it for following reasons.

  • Using if/List method requires every method that call this method check for errors, not only that but it requires everybody remember to check for errors, and if somebody forget, then they will have error
  • You can have Application level exception handler that will record all exceptions to the repository storage
  • You can catch all exceptions in one place (WCF service, Application) so less code is required for dealing with individual error.
  • Much easier on the caller side to implement call just wrap call in try/catch. More readable code.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.