fin
fin

Reputation: 1303

How can I make this exception handling code adhere to the DRY principle?

I have a particular situation where I need to trap exceptions and return an object to the client in place of the exception. I cannot put the exception handling logic at a higher level i.e. wrap Foo within a try clause.

It's best to demonstrate with some sample code. The exception handling logic is clouding the intention of the method and if I have, many methods of similar intent, in the Foo class, I find myself repeating most of the catch logic.

What would be the best technique to wrap the common exception functionality in the code below?

public class Foo
{
     public Bar SomeMethodThatCanThrowExcepetion()
     {
          try
          {
              return new Bar().Execute();
          }
          catch(BazException ex)
          {
              WriteLogMessage(ex, Bar.ErrorCode);
              return new Bar() { ErrorMessage = ex.Message, ErrorCode = Bar.ErrorCode;}                  
          }
     }

     public Baz SomeMethodThatCanThrowExcepetion(SomeObject stuff)
     {
          try
          {
              return new Baz(stuff).Execute();
          }
          catch(BazException ex)
          {
              WriteLogMessage(ex, Baz.ErrorCode);
              return new Baz() { ErrorMessage = ex.Message, ErrorCode = Baz.ErrorCode;}                  
          }
     }
 } 

Upvotes: 8

Views: 512

Answers (4)

James
James

Reputation: 7543

How about a base class:

public class ErrorCapable {
  public string ErrorMessage { set; get; }
  public int ErrorCode { set; get; }

  public static ErrorCapable<T> Oops(Exception exc) where T : ErrorCapable, new() {
    // Code for logging error here
    return new T() { ErrorMessage = exc.Message, ErrorCode = exc.ErrorCode };
  }
}

public class Bar : ErrorCapable {
  //...
}
public class Baz : ErrorCapable {
  //...
}

Then in the catch, just use, for example:

return ErrorCapable.Oops<Bar>(ex);

Upvotes: 5

McGarnagle
McGarnagle

Reputation: 102793

Updated per Lee's comment


One possibility is to use a generic helper method. Something like this:

T TryExecute<T>(Func<T> action, int ErrorCode)
{
    try
    {
        return action();
    }
    catch (Exception ex)
    {
        result = Activator.CreateInstance<T>();
        typeof(T).GetProperty("ErrorMessage").SetValue(result, ex.Message, null);
        typeof(T).GetProperty("ErrorCode").SetValue(result, ErrorCode, null);
        return result;
    }
    return result;
}

If you can modify Bar and Baz, then you could improve this by placing a requirement on T:

public interface IError
{
    public string ErrorMessage { get; set; }
    public int ErrorCode { get; set; }
}

T TryExecute<T>(Func<T> action, int ErrorCode) where T : IError
{
    try
    {
        return action();
    }
    catch (Exception ex)
    {
        result = Activator.CreateInstance<T>();
        result.ErrorMessage = ex.Message;
        result.ErrorCode = ErrorCode;
        return result;
    }
}

Then you'd use:

return TryExecute<Bar>(new Bar().Execute, Bar.ErrorCode);

And:

return TryExecute<Baz>(new Baz(stuff).Execute, Baz.ErrorCode);

That may or may not be an over-abstraction for your particular design; the devil is in the details.

Upvotes: 5

Lee
Lee

Reputation: 144206

I think the best you can do is something like:

public T TryOrDefault<T>(Func<T> act, int errorCode, Func<BazException, T> onError)
{
    try
    {
        return act;
    }
    catch(BazException ex)
    {
        WriteLogMessage(ex, errorCode);
        return onError(ex);
    }
}

then you can write your other methods in terms of it:

public Bar SomeMethodThatCanThrowException()
{
    Bar b = new Bar();
    return ExecOrDefault(() => b.Execute(), Bar.ErrorCode, ex => new Bar { ErrorMessage = ex.Message, ErrorCode = Bar.ErrorCode });
}

Upvotes: 2

Femaref
Femaref

Reputation: 61477

Do you really need the explicit logging in every method? Instead of having the exception logic in every method, have one handler in your Main method of the program and handle the exceptions generically.

Also, you don't need to return an arbitrary object from a catch block should you really need the logging there, simply use throw; to let it wander up the stack.

Upvotes: 2

Related Questions