Monday, October 31, 2005

Passing complex object graphs through web services

The web method must be attributed as SoapRpcMethod and all the types that occur in the object graph must be defined by using BOTH XmlInclude AND SoapInclude attributes:
Public Function GetPersons() As Person()
    ' code to return person
End Function
The client proxy class must have SoapInclude attributes for all types in the object graph:

System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Web.Services.WebServiceBindingAttribute(Name:="PersonServiceSoap", [Namespace]:=""), _ 
SoapInclude(GetType(Person)), _
SoapInclude(GetType(Nurse)), _ 
SoapInclude(GetType(Doctor))> _
Public Class PersonService    
Inherits System.Web.Services.Protocols.SoapHttpClientProtocol
    ' implementation
End Class
When you generate a proxy class using WSDL.exe (via Visual Studio too) it will generate dummy versions of all the classes described in the WSDL file. You need to delete these and make sure your proxy class has a reference to your domain objects so that it deserializes the soap message as the correct types. Passing object graphs through web services puts limitations on your object design. Rocky Lhotka discusses this here This is because of the way objects are serialized by web services. You have to have a default constructor and all properties have to be gettable and settable. This is unfortunate because it means you can’t do good object oriented design. An alternative is to use the binary serializer and then pass the resulting byte array using web services, but then you loose the interoperability that web services give you.