Question

Let's imagine we have a service CustomerService:

public class CustomerService
{
    public Customer GetCustomer(string customerName)
    {
        if(string.IsNullOrWhiteSpace(customerName))
            throw new ArgumentNullException(nameof(customerName));

        try
        {
            var customer = customerRepository.GetByName(customerName);
            return customer;
        }
        catch(Exception ex)
        {
            _logger.Error(ex);
            throw;
        }
    }
}

This service is used in a controller MyController:

public class MyController : Controller
{
     /* Properties */

     public ActionResult CustomerStuff(string customerName)
     {
          try
          {
              var customer = _customerService.GetCustomer(customerName);
              return View(customer);
          }
          catch (Exception ex)
          {
              // Redirect to error page, etc...
          }
     }
}

The problem with this is that I hide possible ArgumentNullException which is probably caused by a bug in my code. I never expect anything to pass null to GetCustomer, but catch(Exception) in MyController hides this exception with the rest of exceptions. On the other hand, on production I want to redirect users if something goes wrong in the CustomerService, because e.g. connection timeout occured.

How can this problem be resolved without tons of code, such as:

try
{

}
catch (Exception ex) when (!(ex is ArgumentNullException))
{
    // handle timeouts, network availability, etc..
}

One possible solution I can see is creating a custom exception, such as ServiceException and throwing it inside try/catch in GetCustomer method. Is there any other technique that is maybe more common?

Was it helpful?

Solution

Just change your service to:

public class CustomerService
{
    public Customer GetCustomer(string customerName)
    {
        try
        {
            if(string.IsNullOrWhiteSpace(customerName))
                throw new ArgumentNullException(nameof(customerName));

            var customer = customerRepository.GetByName(customerName);
            return customer;
        }
        catch(Exception ex)
        {
            _logger.Error(ex);
            throw;
        }
    }
}

That way, your logger catches the argument error, along with all other errors. The controller then needs no special logic to handle specific exceptions.

Licensed under: CC-BY-SA with attribution
scroll top