1

Here's what I observe

I need to throw a custom Exception subtype from the service to the client. (Listed as a FaultContract on the specific operation). I have certain fields on the CustomException, that should be received by the client.

[Serializable]
class MyCustomException : Exception
{
    public string From { get; private set; }

    public MyCustomException(string where)
    {
        From = where;
    }

}

}

I find that the field isn't being deserialized even though the exception is present inside the FaultException instance. I tried implementing ISerializable by overriding GetObjectData and the serialization ctor, but no dice. The only way I could get it across was changing MyCustomException to be a DataContract and not derive from Exception.

[DataContract]
class MyCustomException
{
    [DataMember]
    public string From { get; private set; }

    public MyCustomException(string where)
    {
        From = where;
    }
}

This works. However it can't be derived from Exception anymore.. since Exception is marked with the Serializable attribute and you can't have both Serializable and DataContract on a type. (Confirmed: run time Exception thrown)

So my question is : What is the right way to propogate the fields of a custom exception subtype in WCF?

Gishu
  • 134,492
  • 47
  • 225
  • 308

2 Answers2

2

... you can't have both Serializable and DataContract on a type. (Confirmed: run time Exception thrown)

Firstly, you can have DataContract and Serializable together. Infact, without this, the website I am working on now will not work, since my WCF service is consumed over the web via $.ajax. This SO thread will provide you the formal details.

Next, I strongly suggest not to inherit your custom fault from Exception. Reason is you already have a FaultException<TDetail> built-in class, where TDetail is your custom fault. You can read this MSDN article for implementation details - remember to turn off 'exception details' at the client while deploying.

catch (FaultException<MyFault> e)
{
    //e is the full exception (with StackTrace et al.) when 'exception details' is on
    //e.Detail is your custom fault which is always available
}
Community
  • 1
  • 1
Channs
  • 2,091
  • 1
  • 15
  • 20
  • 1
    This link has the exact error message that I see when I mark an exception subtype with DataContractAttribute. http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/5fdfc1d7-89f2-4f8b-a491-d9ac2cb0600c/ It seems that WCF is supposed to support the ISerializable model, so why doesn't MyCustomException when marked with the SerializableAttribute work ? – Gishu Jul 19 '12 at 07:20
  • Found the answer to my question - Since Base type Exception implements ISerializable.. the derived types also have to comply and override to serialize their specific fields. – Gishu Jul 19 '12 at 10:10
1

Here's how I got it to work.. not sure if it is the right way. I could not find any explicit guideline stating that you're not supposed to use Exception subtypes as the TDetail parameter in FaultException<TDetail>.

I tried throwing a FaultException<FileNotFoundException> and found that the filename field was in fact propogated correctly.

So what's the delta between FileNotFoundException and MyCustomException?

Since the base exception implements ISerializable, I had to override the methods on the server side..

    protected MyCustomException(SerializationInfo info, StreamingContext context) :
        base(info, context)
    {
        this.From = info.GetString("From");
    }
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue("From", this.From);
    }

I then used svcutil to generate the client proxy classes. I find that FileNotFoundException isn't generated (since it is an built-in type) but MyCustomException is. However it is without any fields and has just an empty deserialization ctor.

I did not delve into ISerializable earlier because when I set a breakpoint in the proxy class ctor it wasn't being hit. I incorrectly aborted that line of investigation (Failed to see the DebuggerStepThrough attribute and GeneratedCode attribute on the class; added by svcutil).

So I then manually edit the auto-generated classes to add the field and set it in the ctor like so..

[System.SerializableAttribute()]
    public partial class MyCustomException : System.Exception
    {
        public string From { get; private set; } // manual edit

        public MyCustomException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) :
            base(info, context)
        {
             this.From = info.GetString("From");  // manual edit
        }
    }

Now it works as expected; The field data is intact.

This raises another question: Why doesn't the proxy generation step do this automatically ?

Gishu
  • 134,492
  • 47
  • 225
  • 308