Avrohom Yisroel Avrohom Yisroel - 22 days ago 5
C# Question

Can I return execution to the failing method after an unhandled exception?

I have an ASP.NET web site that houses some WCF services. I have hooked up to the Application_Error event, so can log any unhandled exceptions.

What I would really like to do is pass the execution back to the method that was called, so I can return something sensible to the client, rather than throwing a FaultException.

I know I can wrap each individual service call in a try/catch block, but that means a load of boilerplate code in every single method. What I would really like to do is have a central catch-all method, like Application_Error, but then allow the individual service call do take back control and return something meaningful to the client.

Is this possible?

P.S. In case anyone thinks I'm doing this the wrong way, the background to this is that I'm implementing Railway Oriented Programming in my C# WCF code. The idea is that if there is an exception, I return an appropriate object that contains the details, along with other useful stuff. This is a much more controlled way of dealing with the situation than having the WCF service throw an exception.

Answer

If you want to do something akin to the ROP-style, then you can do the following (Failable name from Jeff Bridgman's suggestion).

First create abstract base classes as follows...

public abstract class Failable<T> {
}
public abstract class Failable {
}

Next create concrete classes from these to represent success and failure. I've added a generic and non-generic version of each, so you can handle service calls that return a value as well as void ones...

public class Success<T> : Failable<T> {
  public Success(T value) {
    Value = value;
  }

  public T Value { get; set; }
}

public class Success : Failable {
}

public class Failure<T> : Failable<T> {
  public Failure(Exception value) {
    Value = value;
  }

  public Exception Value { get; set; }
}

public class Failure : Failable {
  public Failure(Exception value) {
    Value = value;
  }

  public Exception Value { get; set; }
}

Then a couple of helper methods will give you what you want...

public static Failable<T> DoFailableAction<T>(Func<T> f) {
  Failable<T> result;
  try {
    T fResult = f();
    result = new Success<T>(fResult);
  }
  catch (Exception ex) {
    result = new Failure<T>(ex);
  }
  return result;
}

public static Failable DoFailableAction(Action f) {
  Failable result;
  try {
    f();
    result = new Success();
  }
  catch (Exception ex) {
    result = new Failure(ex);
  }
  return result;
}

To use these, suppose you have your WCF service. You wrap the service call in the helper method...

Failable<Person> p = FailableHelpers.DoFailableAction(() => service.GetPerson(1));

Then, you can check if the call succeeded or failed, and act appropriately...

if (p is Success<Person>) {
  Person p = ((Success<Person>)p).Value;
  Debug.WriteLine("Value is Success, and the person is " + p.FirstName + " " + p.Surname);
} else {
  Exception ex = ((Failure<Person>)p).Value;
  Debug.WriteLine("Value is Failure, and the message is " + ex.Message);
}

If your service method is void, you just use the non-generic variation...

Person jim = new Person(1, "Jim", "Spriggs");
Failable saveResult = FailableHelpers.DoFailableAction(() => service.Update(jim));

You haven't quite achieved what you wanted, as you still have some boilerplate code in each service method, but it's very little. Not as little as an attribute on the method, but then this approach is much simpler to implement, and works for any kind of level, whether a repository, business logic, WCF service, client, etc. The attibute approach only works for WCF.

The benefit of this approach is that it puts the onus on the consumer of the WCF service (or whatever) to check if the service call was successful, and act appropriately. This is a functional style, and leads to much more robust coding, as you are pretty much forced not to ignore exceptions, unlike non-functional programming, where you would likely just grab the result of the service under the assumption that nothing went wrong. That's fine until something does go wrong!

Hope that helps.

Comments