Search code examples
xmlweb-serviceswcfwsdlfor-xml-path

Is it bad practice to directly generate XML for sending to a WCF web service?


Before I was given the wsdl for a web service, I had already generated the xml using SQL and FOR XML PATH.

Now I have the wsdl, should I go back and populate each object individually from SQL, should I deserialise my XML into the request object (if that is possible) or is there another choice? Any gotchas you can warn me about in advance?


Solution

  • As far as I can tell if you are posting a complicated xml structure from a SQL database to a WCF web service then there is no harm in creating the whole xml document using FOR XML PATH - in fact I would say it was better and simpler. You can then manually validate it against the xsd (which WILL validate against xsd:restrictions, minOccurs, maxOcurs etc) and then deserialise it into the complex object hierarchy created by the WSDL. Note that the WSDL does not do any validations (xsd:restrictions, minOccurs, maxOccurs are not validated on the client side) - it just creates the object hierarchy.

    This assumes you are comfortable with SQL and XML PATH of course! Otherwise you can build the object hierarchy up in code, but you won't have any validation until you call the real web service.

    The xsd validation is a belt and braces backup to avoid sending invalid data to the web service. Clearly, in the real world, whatever data you are sending should be validated properly with user friendly error messages.

    Here's some sample code:

    Imports System.Xml
    Imports System.Xml.Schema
    Imports System.Data.SqlClient
    Imports System.Xml.Serialization
    Imports ClassLibrary1.ServiceReferences.ClientX
    Imports System.IO
    Imports System.Configuration
    
    Public Class ClientXWebServices
    
    Private _isValid As Boolean?
    Private _xmlErrorList As New List(Of String)
    Private _xmlWarningList As New List(Of String)
    
    Sub New()
    
    End Sub
    
    Function Send(id As Guid) As StoreData.storeDataResponse
        Dim xdoc As XmlDocument = New XmlDocument()
        Using cnn As New SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString())
            cnn.Open()
            Dim cmd = New SqlCommand("employer.clientx_xml_setup_select", cnn)
            cmd.CommandType = CommandType.StoredProcedure
            cmd.Parameters.AddWithValue("@id", id)
    
            Using reader = cmd.ExecuteXmlReader
                If (reader.Read()) Then
                    xdoc.Load(reader)
                End If
            End Using
        End Using
    
        'Validate (belt and braces)
        Dim myschema As XmlSchema
        Using reader As XmlTextReader = New XmlTextReader("storeData.xsd")
            myschema = XmlSchema.Read(reader, Nothing)
        End Using
        xdoc.Schemas.Add(myschema)
        xdoc.Validate(AddressOf DocumentValidationCallback)
        If Not _isValid.HasValue Then
            _isValid = True
        End If
        If _isValid = False Then
            MsgBox(String.Join(vbCrLf, _xmlErrorList.Union(_xmlWarningList).ToArray))
            Return Nothing
        End If
    
        Dim xmlSer As XmlSerializer = New XmlSerializer(GetType(StoreData.storeDataRequest))
        Dim ssdr As StoreData.storeDataRequest = xmlSer.Deserialize(New StringReader(xdoc.OuterXml))
        Dim client As New StoreData.StoreDataClient
        Dim response As StoreData.storeDataResponse
        Try
            response = client.ServiceReferences_CLIENTX_StoreData_StoreData_storeData(ssdr)
        Catch ex As Exception
            Throw New IOException("ClientX Web Service 'StoreData' could not be contacted.", ex)
        End Try
    
        Return response
    End Function
    
    Sub DocumentValidationCallback(ByVal sender As Object, ByVal args As ValidationEventArgs)
        If args.Severity = XmlSeverityType.Warning Then
            _xmlErrorList.Add(args.Message)
        ElseIf args.Severity = XmlSeverityType.Error Then
            _xmlWarningList.Add(args.Message)
        End If
        _isValid = False
    End Sub
    

    End Class