Brecht Yperman
Brecht Yperman

Reputation: 1279

Call WCF service with XML

I'm trying to call a WCF service.

However, I have the request body as an XML document.

So for instance, instead of this

ListProductsRequest request = new ListProductsRequest();
request.Query = new RootQuery();
request.Query.Product = "milk";
request.Query.Group = "dairy";

ListProductsPortType client = new ListProductsPortTypeClient();
ListProductsResponse response = client.ListProducts(request);

I wanna do this:

String xml = "<Root xmlns=\"urn:ns\"><Query><Group>dairy</Group><Product>milk</Product></Query></Root>";
var request = // read in to XmlReader or XmlDocument or whatever
ListProductsPortType client = new ListProductsPortTypeClient();
var response = client.ListProducts(request);

Is there a way to use the generated proxy, with the advantage of having the data layer security and transport handled for me, but without using the proxy objects?

Thanks, Brecht

Upvotes: 4

Views: 4477

Answers (3)

Brecht Yperman
Brecht Yperman

Reputation: 1279

I got this far, thanks to 2GDev's comment. Code without handling exceptions or abnormal situations properly.

This way I can use the generated stub's endpoint (and thus reuse the config etc.)

    public void CallWs()
    {
        WsdlRDListProductsPortTypeClient client = new WsdlRDListProductsPortTypeClient();

        String req = "<Root xmlns=\"urn:ns\"><Query><Group>TV</Group><Product>TV</Product></Query></Root>";

        CallWs(client.Endpoint, "ListProducts", GetRequestXml(req));
    }

    public XmlElement CallWs(ServiceEndpoint endpoint, String operation, XmlElement request)
    {
        String soapAction = GetSoapAction(endpoint, operation);

        IChannelFactory<IRequestChannel> factory = null;

        try
        {
            factory = endpoint.Binding.BuildChannelFactory<IRequestChannel>();
            factory.Open();
            IRequestChannel channel = null;

            try
            {
                channel = factory.CreateChannel(endpoint.Address);
                channel.Open();

                Message requestMsg = Message.CreateMessage(endpoint.Binding.MessageVersion, soapAction, request);
                Message response = channel.Request(requestMsg);
                return response.GetBody<XmlElement>();
            }
            finally
            {
                if (channel != null)
                    channel.Close();
            }
        }
        finally
        {
            if (factory != null)
                factory.Close();
        }
    }

    private String GetSoapAction(ServiceEndpoint endpoint, String operation)
    {

        foreach (OperationDescription opD in endpoint.Contract.Operations)
        {
            if (opD.Name == operation)
            {
                foreach (MessageDescription msgD in opD.Messages)
                    if (msgD.Direction == MessageDirection.Input)
                    {
                        return msgD.Action;
                    }
            }

        }

        return null;
    }

When I try this with the basic ICalculator sample from msdn http://msdn.microsoft.com/en-us/library/ms734712.aspx

Which is secured with SPNego, I have to change this a bit, because then we need an IRequestSessionChannel instead of an IRequestChannel.

    public XmlElement CallWs(ServiceEndpoint endpoint, String operation, XmlElement request)
    {
        String soapAction = GetSoapAction(endpoint, operation);

        IChannelFactory<IRequestSessionChannel> factory = null;

        try
        {


            factory = endpoint.Binding.BuildChannelFactory<IRequestSessionChannel>();
            factory.Open();
            IRequestSessionChannel channel = null;

            try
            {
                channel = factory.CreateChannel(endpoint.Address);
                channel.Open();

                Message requestMsg = Message.CreateMessage(endpoint.Binding.MessageVersion, soapAction, request);

                Message response = channel.Request(requestMsg);
                return response.GetBody<XmlElement>();
            }
            finally
            {
                if (channel != null)
                    channel.Close();
            }
        }
        finally
        {
            if (factory != null)
                factory.Close();
        }
    }

It does do the negotiation, and a message seems to be sent, but unfortunately I now get the following error message:

No signature message parts were specified for messages with the 'http://Microsoft.ServiceModel.Samples/ICalculator/Add' action.

Upvotes: 1

eFloh
eFloh

Reputation: 2158

I think you could use

ListProductsRequest request = (ListProductsRequest) new XmlSerializer(
    typeof(ListProductsRequest)).Deserialize();

to create the corresponding object...

Upvotes: 0

2GDev
2GDev

Reputation: 2466

I don't think you can call a WCF service and passing what you want.

The method ListProducts only accept a ListProductsRequest object. So you have to create this kind of object.

String xml = "<Root xmlns=\"urn:ns\"><Query><Group>dairy</Group><Product>milk</Product></Query></Root>";
ListProductsRequest request = MappingObject(xml); 
ListProductsPortType client = new ListProductsPortTypeClient();
var response = client.ListProducts(request);

And in the Mapping method you can work with your XML to create an ListproductRequest.

I don't know if there is another way to do this.

Upvotes: 1

Related Questions