WCF Silverlight: Exception and Serialization
Exception is used as a mechanism to report issue by an application. In WCF webservice implementation, an application may throw exception which will be wrapped with Fault and sends over to the client. However, if an application chooses to return Exception as parameter instead, the exception needs to be serialized. I like to describe how the exception serialization works, how the import tool (svcutil/slsvcutil) generates codes for those classes and what is the different between .Net Framework/Silverlight implementation.
WCF’s DataContractSerializer serialize s Exception class via ISerializable (Exception in .Net Framework implements ISerializable). However, in Silverlight, there is no ISerializable interface. Therefore, Serialization information of Exception transferred over the wire will not be set into the Exception class. This does not only apply to Exception but also any types implementing ISerializable on the .Net Framework serialized to Silverlight. Siverlight proxy generation works around the issue by generating a type called ArrayOfXElement instead to capture all information as XElements (code snippet below).
[System.Xml.Serialization.XmlSchemaProviderAttribute(null, IsAny = true)] [System.CodeDom.Compiler.GeneratedCodeAttribute("slsvcutil", "2.0.5.0")] public class ArrayOfXElement : System.Xml.Serialization.IXmlSerializable { private System.Collections.Generic.List<System.Xml.Linq.XElement> nodes = new System.Collections.Generic.List<System.Xml.Linq.XElement>(); public ArrayOfXElement() { } public virtual System.Collections.Generic.List<System.Xml.Linq.XElement> Nodes { get { return this.nodes; } } public virtual System.Xml.Schema.XmlSchema GetSchema() { throw new System.NotImplementedException(); } public virtual void WriteXml(System.Xml.XmlWriter writer) { System.Collections.Generic.IEnumerator<System.Xml.Linq.XElement> e = nodes.GetEnumerator(); for ( ; e.MoveNext(); ) { ((System.Xml.Serialization.IXmlSerializable)(e.Current)).WriteXml(writer); } } public virtual void ReadXml(System.Xml.XmlReader reader) { for ( ; (reader.NodeType != System.Xml.XmlNodeType.EndElement); ) { if ((reader.NodeType == System.Xml.XmlNodeType.Element)) { System.Xml.Linq.XElement elem = new System.Xml.Linq.XElement("default"); ((System.Xml.Serialization.IXmlSerializable)(elem)).ReadXml(reader); Nodes.Add(elem); } else { reader.Skip(); } } } } |
Let’s discuss this in two scenarios
1) Webservice call returning Exceptions defined in mscorlib.dll and System.ServiceModel.dll assemblies.
2) Webservice call returning other Exceptions (eg. MyCustomException).
1) Webservice call returning Exceptions defined in mscorlib.dll and System.ServiceModel.dll assemblies.
[ServiceContract] public interface IEchoService { [OperationContract] Exception GetException(); [OperationContract] ArgumentException GetArgumentException(); [OperationContract] FaultException GetFaultException(); } |
By default, the webservice importing tools such as svcutil/slsvcutil (or VS’s Add Service Reference…) add references to mscorlib.dll and System.ServiceModel.dll assembly. Hence, the tool will not generate any codes for those types. You may, however, pass the flag /nostdlib to not include those assemblies as references and the type will be generated.
Below shows codes generated for .Net Framework for Exception class.
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")] [System.SerializableAttribute()] public partial class Exception : object, System.Runtime.Serialization.ISerializable { private System.Runtime.Serialization.SerializationInfo info; public Exception(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { this.info = info; } public System.Runtime.Serialization.SerializationInfo SerializationInfo { get { return this.info; } set { this.info = value; } } public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { if ((this.SerializationInfo == null)) { return; } System.Runtime.Serialization.SerializationInfoEnumerator enumerator = this.SerializationInfo.GetEnumerator(); for ( ; enumerator.MoveNext(); ) { System.Runtime.Serialization.SerializationEntry entry = enumerator.Current; info.AddValue(entry.Name, entry.Value); } } } |
Code generated for Silverlight will simply be ArrayOfXElement as shown earlier.
Note: One limitation of ISerializable generated type is it relies on the code namespace as its namespace uri. If you try to change the namespace such as using /namespace flag, the import will fail with the below error.
Error: Cannot import wsdl:portType Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter Error: ISerializable type with data contract name 'Exception' in namespace 'https://schemas.datacontract.org/2004/07/System' cannot be imported. The data contract namespace cannot be customized for ISerializable types and the generated namespace 'wcf' does not match the required CLR namespace 'System'. Check if the required namespace has been mapped to a different data contract namespace and consider mapping it explicitly using the namespaces collection. XPath to Error Source: //wsdl:definitions[@targetNamespace='https://tempuri.org/']/wsdl:portType[@name='IService'] |
2) Webservice call returning other Exceptions (eg. MyCustomException).
An application may define a custom Exception or use an Exception defined in other assembly besides mscorlib and System.ServiceModel.
[ServiceContract] public interface IEchoService { [OperationContract] MyCustomException GetCustomException(); } |
In this case, that exception class will be generated.
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")] [System.SerializableAttribute()] public partial class MyCustomException : System.Exception { public MyCustomException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } |
Code generated for Silverlight will simply strip out SerializableAttribute and the ctor with SerializationInfo (not exist in Silverlight).
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")] public partial class MyCustomException : System.Exception { } |
Notice that the class is generated deriving from System.Exception since mscorlib is referenced by default. If you specify /nostdlib explicitly, the System.Exception class will also be generated and, in Silverlight, all ISerializable types will be replaced with ArrayOfXElement accordingly.
Note: you may explicitly reference ( /reference) to an assembly defining MyCustomException class to avoid the code generation altogether.
Comments
- Anonymous
January 21, 2009
PingBack from http://blog.a-foton.ru/index.php/2009/01/22/wcf-silverlight-exception-and-serialization/