How to get service reference to generate correctly with message contracts based on 3rd party WSDL, or force no message contracts in WF Service project

I am far from an authority on these issues, and while this response below might not be an exact fit to your problem, my recent experience of making a proxyless connection to a service might offer some insight to you or the next person with a similar issue.

I would start by seeing if you can hand roll the SOAP request using fiddler, and see if you are able to create the correct message and send that along. Since you describe the automation tools as being buggy (or perhaps there is a config issue you’re not getting just so). Either way, having a clear understanding of the shape of the contract, and being able to perform a reliable test in fiddler may offer clarity.

You don’t necessarily need to rely on using a proxy. You can create your own soap message and send it along one of two ways. The first is using the ChannelFactory.

  1. Create your message body (if required, message class can work w/out one)
  2. Create your message
  3. Send your message via the ChannelFactory

For step 1 you can construct your message by making a simple POCO to mirror what is expected in your contract. You should be able to derive what that class is via the WSDL.

Let’s say the service is something like this:

[ServiceContract(Namespace = "http://Foo.bar.car")]
public interface IPolicyService
{
    [OperationContract]
    PolicyResponse GetPolicyData(PolicyRequest request);
}

public class PolicyData : IPolicyService
{
   public PolicyResponse GetPolicyData(PolicyRequest request)
   {
            var polNbr = request.REQ_POL_NBR;
            return GetMyData(polNbr);
    }
}

You would need a class something like this:

[DataContract(Namespace = "http://Foo.bar.car")]
public class GetPolicyData
{
    [DataMember]
    public request request { get; set; }
}

[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy")]
public class request
{
    ///<summary>
    /// Define request parameter for SOAP API to retrieve selective Policy level data
    /// </summary>
    [DataMember]
    public string REQ_POL_NBR { get; set; }
}

and then you would call it like this:

    private static Message SendMessage(string id)
    {
        var body = new GetPolicyData {request =  new request{ REQ_POL_NBR = id }}; 
        var message = Message.CreateMessage(MessageVersion.Soap11, "http://Foo.bar.car/IPolicyService/GetPolicyData", body);
// these headers would probably not be required, but added for completeness
        message.Headers.Add(MessageHeader.CreateHeader("Accept-Header", string.Empty, "application/xml+"));
        message.Headers.Add(MessageHeader.CreateHeader("Content-Type", string.Empty, "text/xml"));
        message.Headers.Add(MessageHeader.CreateHeader("FromSender", string.Empty, "DispatchMessage"));
        message.Headers.To = new System.Uri(@"http://localhost:5050/PolicyService.svc");

        var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
        {
             MessageEncoding = WSMessageEncoding.Text,
            MaxReceivedMessageSize = int.MaxValue,
            SendTimeout = new TimeSpan(1, 0, 0),
            ReaderQuotas = { MaxStringContentLength = int.MaxValue, MaxArrayLength = int.MaxValue, MaxDepth = int.MaxValue }
        };
        message.Properties.Add("Content-Type", "text/xml; charset=utf-8");
        message.Properties.Remove("Accept-Encoding");
        message.Properties.Add("Accept-Header", "application/xml+");

        var cf = new ChannelFactory<IRequestChannel>(binding, new EndpointAddress(new Uri("http://localhost:5050/PolicyService.svc")));

        cf.Open();
        var channel = cf.CreateChannel();
        channel.Open();

        var result = channel.Request(message);

        channel.Close();
        cf.Close();
        return result;
    }

What you receive back will be a Message, which you will need to deserialize, and there are a few OOTB ways of doing this, (Message.GetReaderAtBodyContents, Message.GetBody) in keeping w/the hand-rolled theme:

    /// <summary>
/// Class MessageTransform.
/// </summary>
public static class MessageTransform
{
    /// <summary>
    /// Gets the envelope.
    /// </summary>
    /// <param name="message">The message.</param>
    /// <returns>XDocument.</returns>
    public static XDocument GetEnvelope(Message message)
    {
        using (var memoryStream = new MemoryStream())
        {
            var messageBuffer = message.CreateBufferedCopy(int.MaxValue);
            var xPathNavigator = messageBuffer.CreateNavigator();

            var xmlWriter = XmlWriter.Create(memoryStream);
            xPathNavigator.WriteSubtree(xmlWriter);
            xmlWriter.Flush();
            xmlWriter.Close();

            memoryStream.Position = 0;
            var xdoc = XDocument.Load(XmlReader.Create(memoryStream));
            return xdoc;
        }           
    }

    /// <summary>
    /// Gets the header.
    /// </summary>
    /// <param name="message">The message.</param>
    /// <returns>XNode.</returns>
    public static XNode GetHeader(Message message)
    {
        var xdoc = GetEnvelope(message);

        var strElms = xdoc.DescendantNodes();
        var header = strElms.ElementAt(1);

        return header;
    }

    /// <summary>
    /// Gets the body.
    /// </summary>
    /// <param name="message">The message.</param>
    /// <param name="localName">Name of the local.</param>
    /// <param name="namespaceName">Name of the namespace.</param>
    /// <returns>IEnumerable&lt;XElement&gt;.</returns>
    public static IEnumerable<XElement> GetBody(Message message, string localName, string namespaceName)
    {
        var xdoc = GetEnvelope(message);

        var elements = xdoc.Descendants(XName.Get(localName, namespaceName));

        return elements;
    }
}

OR you could build your soap envelope by hand and use WebClient:

using System.Net; 
using System.Xml.Linq;

public static class ClientHelper
{
    public static string Post(string targetUrl, string action, string method, string key, string value)
    {
        var request = BuildEnvelope(method, key, value);
    using (var webClient = new WebClient())
    {
        webClient.Headers.Add("Accept-Header", "application/xml+");
        webClient.Headers.Add("Content-Type", "text/xml; charset=utf-8");
        webClient.Headers.Add("SOAPAction", action);
        var result = webClient.UploadString(targetUrl, "POST", request);

        return result;
    }
}

public static string BuildEnvelope(string method, string key, string value)
{
    XNamespace s = "http://schemas.xmlsoap.org/soap/envelope/";
    XNamespace d = "d4p1";
    XNamespace tempUri = "http://tempuri.org/";
    XNamespace ns = "http://Foo.bar.car";
    XNamespace requestUri = "http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy";
    var xDoc = new XDocument(
                        new XElement(
                            s + "Envelope",
                            new XAttribute(XNamespace.Xmlns + "s", s),
                            new XElement(
                                s + "Body",
                                new XElement(
                                    ns + method,
                                    new XElement(requestUri + "request", 
                                        new XElement(tempUri + key, value))
                                )
                            )
                        )
                    );
    // hack - finish XDoc construction later
    return xDoc.ToString().Replace("request xmlns=", "request xmlns:d4p1=").Replace(key, "d4p1:" + key);
}

which is called with:

return ClientHelper.Post("http://localhost:5050/PolicyService.svc", "http://Foo.bar.car/IPolicyService/GetPolicyData", "GetPolicyData", "REQ_POL_NBR", id);

Testing it in Fiddler would look something like this:

Post action:    http://localhost:5050/PolicyService.svc
Header:
User-Agent: Fiddler
SOAPAction: http://Foo.bar.car/IPolicyService/GetPolicyData
Content-type: text/xml
Host: localhost:5050
Content-Length: 381

Body:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
<GetPolicyData xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://Foo.bar.car">
<request xmlns:d4p1="http://schemas.datacontract.org/2004/07/Foo.bar.car.Model.Policy">
<d4p1:REQ_POL_NBR>1</d4p1:REQ_POL_NBR>
</request>
</GetPolicyData>
  </s:Body>
</s:Envelope>

Again, this answer isn’t trying to resolve how to invoke svcUtil differently, but to avoid calling it altogether, so I hope the edit gods don’t ding me for that 😉

My code above has been inspired by better developers than I, but I hope it helps.

http://blogs.msdn.com/b/stcheng/archive/2009/02/21/wcf-how-to-inspect-and-modify-wcf-message-via-custom-messageinspector.aspx

Leave a Comment