One question I frequently get asked is "how do I handle exceptions over Web Services?"  If you've ever tried passing exceptions using Web Services you'll likely be able to relate to the issues.  The main problem is that the majority of toolkits don't have a great way to deserialize generated SOAP Faults to local exceptions - which means that you have to dig through the SOAP Fault to get an error code or parse the detail message to figure out what when wrong.  Keith Brown recently highlighted some of the problems in a recent post. 

 

Fortunately, this is something that Indigo handles beautifully.  Here's how it works:

 

Firstly, you define your custom exception:

 

[Serializable]

public class MyCustomException : Exception, ISerializable

{

public MyCustomException(string Message)

: base(Message)

{

}

 

public MyCustomException(SerializationInfo si, StreamingContext sc)

: base(si, sc)

{

}

}

 

Notice that the exception is attributed with Serializable, implements ISerializable and has a constructor that accepts SerializationInfo and StreamingContext.  All these three are required to correctly serialize the custom exception type over the wire.

 

In our service contract we attribute the operation that will be generating this exception.  Here we mark the custom exception as being a known type and define it as a fault contract.

 

[OperationContract]

[KnownType(typeof(MyCustomException))]

[FaultContract(typeof(MyCustomException))]

void GiveMeCustomException();

 

To throw the custom exception from an Indigo service, we generate the custom exception - but then wrap the exception in a SOAP Fault.  It is this that is then thrown from the service.

 

MyCustomException mce = new MyCustomException("Custom problem...");

Fault<MyCustomException> fault = new Fault<MyCustomException>(mce);

throw fault;

 

On the client, catching these custom exceptions is very easy.  You need to reference the shared CustomException type in your project and then...

 

try

{

// call the operation that generates the exception

}

catch (Fault<MyCustomException>)

{

Console.WriteLine("It worked!  Custom Exception!");

}

catch (Exception e)

{

Console.WriteLine("Didn't work.  Sorry.  "+e.ToString());

}

 

Then, to map the fault to the local custom exception, you use:

 

catch (Fault<MyCustomException> fault)

{

Console.WriteLine("It worked!  Custom Exception!");

MyCustomException mce = fault.Detail;

}

 

Finally, you can also add additional fields to the exception (which was Keith's main gripe).  To do this, simply create a public property and override the GetObjectData method to handle the serialization correctly:

 

[Serializable]

public class MyCustomException : Exception, ISerializable

{

private string myValue = "";

 

public MyCustomException(string Message)

: base(Message)

{

}

 

public MyCustomException(SerializationInfo si, StreamingContext sc)

: base(si, sc)

{

}

 

public string MyValue

{

get { return myValue; }

set { myValue = value; }

}

 

public override void GetObjectData(SerializationInfo si, StreamingContext sc)

{

base.GetObjectData(si, sc);

si.AddValue("MyValue", myValue);

}

}

 

From what I've seen, this is an awesome feature and will greatly help the handling of custom exceptions.  Thanks go to Israel Burman for helping me get to the bottom of this.

 

My next task is to see whether I can use the same approach, but map custom exceptions generated on WebSphere 6.0 to custom exceptions in Indigo ;-).