PGCodeWorks"/
This Month
April 2008
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Month Archive
Search
Main Page  »  WCF
View Article  WCF Exceptions and Fault Contracts

I ran across an issue recently trying to get a custom exception across a WCF boundary. The trouble is, WCF does not like to tell you what the problem was, and for good reason. I was thinking about exceptions the wrong way. In reality we do not want to pass exceptions across a WCF service boundary, instead we want to pass a Fault back to the caller.

An exception is a CLR concept, it does not make sense to expose this outside of the CLR, despite the fact that an exception contains potentially dangerous information (like the stack trace) which we do not want to expose. If your service does not handle an exception, it will fault. Hence a lot of the time we implement a try-catch handler and raise a FaultException which gets sent across the boundary.

 

 

[OperationContract]

[FaultContract(typeof(DivideByZeroException))]

public void MyServiceMethod() {

    try {

 

        // Do some actual stuff

    }

    catch(DivideByZeroException ex) {

        throw new FaultException<DivideByZeroException>(ex, new FaultReason("DivisionByZero"));

    }

}

Then on your proxy side you will typically recreate this exception and throw it.

This approach sometimes has problems when your exception has some extra data in it. Consider the following custom exception type:

 

public class MyDivideByZeroException : DivideByZeroException, ISerializable {

 

    protected MyDivideByZeroException(SerializationInfo info, StreamingContext context)

        : base(info, context) {

        numerator = (int)info.GetValue("numerator", typeof(int));

        denominator = (int)info.GetValue("denominator", typeof(int));

    }

 

    private int numerator;

    public int Numerator {

        get { return numerator; }

        set{ numerator = value;}

    }

 

    public int denominator;

    public int Denominator {

        get { return denominator; }

        set { denominator = value; }

    }

 

    public MyDivideByZeroException(int numerator, int denominator) : base() {

        this.numerator = numerator;

        this.denominator = denominator;

    }

 

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {

        info.AddValue("numerator", numerator, typeof(int));

        info.AddValue("denominator", denominator, typeof(int));

        base.GetObjectData(info, context);

    }

}

When you try this with the FaultException you may get an exception like the following:
The underlying secure session has faulted before the reliable session fully completed. The reliable session was faulted. The reliable messaging channel threw an exception because the reliable session was broken.

In this case you can create a Fault class. Your fault class is simply a Data Contract serializable type containing the information you require to create your exception on the other side. e.g.

 

[DataContract]

public class DivideByZeroFault {

 

    public DivideByZeroFault(MyDivideByZeroException exception) {

        this.numerator = exception.Numerator;

        this.denominator = exception.Denominator;

    }

 

    public MyDivideByZeroException GetException() {

        return new MyDivideByZeroException(numerator, denominator);

    }

 

    [DataMember]

    private int numerator;

    public int Numerator {

        get { return numerator; }

        set { numerator = value; }

    }

 

    [DataMember]

    public int denominator;

    public int Denominator {

        get { return denominator; }

        set { denominator = value; }

    }

}

 

Therefore your service code in the first example will change to be:

[OperationContract]

[FaultContract(typeof(DivideByZeroFault))]

public void MyServiceMethod() {

    try {

 

        // Do some actual stuff

    }

    catch(MyDivideByZeroException ex) {

        throw new FaultException<DivideByZeroFault>(new DivideByZeroFault(ex), new FaultReason("DivisionByZero"));

    }

}

 

This will allow WCF to send across the relevant information without compromising the applications security by sending a stack trace etc.

Pete

View Article  WCF: Custom Collections with Extra Payload

I came across a pretty annoying problem to solve in WCF today. Suppose you have a custom collection of items you wish to use in a WCF contract but it also has extra properties that you have added, for example change tracking.

    [Serializable]

    public class DtoCollection<T> : List<T>

    {

        private List<T> addedItems;

        private List<T> removedItems;

    }

 

It will serialize the collection fine but your added properties and fields will not appear. The [CollectionDataContract] attribute will not help you. If you look at this really great post from Sowmy Srinivasan's blog you can see the order of preference for WCF serialization markup. So in the case above you can implement IXmlSerializable to customize the serialization and deserialization of your custom collection. I originally found this solution on David Foderick's blog - OnMaterialize(), but the link seems to be down at the moment. I'll not repost the code here but google cache should still have it at this link.