Freigeben über


Using WCF classes to consume a WebService using Plain Old XML (POX) in a Windows Store app

You can leverage WCF functionality from a Windows Store app to call a WebService using Plain Old XML (POX) style of messaging. This blog and the sample included below documents the steps a Windows Store app should perform to consume such a service.

The sample is based off the topic of Interoperability with POX applications mentioned here: https://msdn.microsoft.com/en-us/library/aa738456.aspx Since Windows Store apps do not have an app.config file, this sample shows how a Windows Store app creates a CustomBinding through code. The CustomBinding is created in such a way that we do not use any Message Encoding and just use plain XML to transmit data. The app calls the Web method synchronously, but you can change it to call asynchronously using the BeginRequest/EndRequest methods.

The blog only covers the Windows Store app implementation and does not cover the implementation of the service. The service code included with this sample exposes the Add, Subtract, Multiply and Divide operations of a Calculator and requires the client to communicate with it using the following XML input body: 

 <Calculator>
  <Method>Add</<Method>
  <d1>2.1</d1>
  <d2>3.2</d2>
</Calculator>

Likewise, the response from the service has the following XML output payload: 

 <CalculatorServiceResponse>
  <Method>Add</Method>
  <ReturnValue>5.3</ReturnValue>
</CalculatorServiceResponse>

The Windows Store app communicates with the Service using the below steps.

1.)    We start off with a “Blank App (XAML)” template using the Visual C# language and name it POXMessagingClient.

2.)    With the blank project created, we add a Class called Calculator.cs in the client project. This class defines the input XML that the WebService expects (see above), so we will create the class with the same structure like this: 

     [System.Runtime.Serialization.DataContract(Namespace = "https://tempuri.org/Calculator")]
    class Calculator
    {
        [System.Runtime.Serialization.DataMember]
        public string Method;
        [System.Runtime.Serialization.DataMember]
        public double d1;
        [System.Runtime.Serialization.DataMember]
        public double d2;
    }

3.)    Similarly, we create a class that will correspond with the WebService response/ output format. To do that, create a class and name is CalculatorServiceResponse.cs and add the following contents:

     [System.Runtime.Serialization.DataContract(Namespace = "https://tempuri.org/CalculatorServiceResponse")]
    public class CalculatorServiceResponse
    {
        [System.Runtime.Serialization.DataMember]
        public String Method;
        [System.Runtime.Serialization.DataMember]
        public String ReturnValue;
    }

4.)    With the input and output structures declared, the next step is to implement a class that will actually call into the remote WebService. Add a class called CustomCalculatorClient.cs and then inherit it from the ClientBase<IRequestChannel> class. You also need to add the references to System.ServiceModel and System.ServiceModel.Channels as follows: 

     using System.ServiceModel;
    using System.ServiceModel.Channels;
    ...
    class CustomCalculatorClient : ClientBase<IRequestChannel>
    {
    }

5.)    Add a method called CreatePoxBinding that will generate a CustomBinding used to invoke the WebService. In this function we first create a TextMessageEncodingBindingElement and pass the first parameter to its constructor as MessageVersion.None. This indicates that a SOAP envelope will not be required. Then we create an HttpTransportBindingElement and then create a CustomBinding instance and pass in the 2 binding elements we just created. The function looks like this: 

         private static Binding CreatePoxBinding()
        {
            TextMessageEncodingBindingElement encoder = new TextMessageEncodingBindingElement(MessageVersion.None, Encoding.UTF8);

            HttpTransportBindingElement transport = new HttpTransportBindingElement();
            transport.ManualAddressing = true;

            return new CustomBinding(new BindingElement[] { encoder, transport });
        }

6.)    To associate this CustomBinding with our class, we add a constructor to our CustomCalculatorClient like this: 

         public CustomCalculatorClient(Uri baseUri)
            : base(CustomCalculatorClient.CreatePoxBinding(), new EndpointAddress( baseUri ) )
        {
        }

7.)    Add a method called PostAsync that will create the underlying WCF Channel request and call the remote service asynchronously. In this function call Message.CreateMessage where we pass in the entityBody for the request. The entityBody parameter will be an instance of the Calculator class with the Method, d1 and d2 parameters that the service expects. The actual conversion of an object to an XML payload will be handled internally. The functions look like this: 

         public IAsyncResult PostAsync(Uri requestUri, object entityBody, AsyncCallback callback, object state)
        {
            Message request = Message.CreateMessage(MessageVersion.None, String.Empty, entityBody);
            request.Headers.To = requestUri;

            HttpRequestMessageProperty property = new HttpRequestMessageProperty();
            property.Method = "POST";

            request.Properties.Add(HttpRequestMessageProperty.Name, property);
            return this.Channel.BeginRequest(request, callback, state);
        }

8.) Notice that since this function is asynchronous, the BeginRequest function expects a callback function as well as a state object that will be used when the function call completes. These parameters are passed from the calling functions in MainPage.xaml. The callback function is called RespCallback and you can find it in the MainPage.xaml page and its implementation is as follows:

         private async void RespCallback(IAsyncResult asyncResult)
        {
            try
            {
                IRequestChannel channel = (IRequestChannel)asyncResult.AsyncState; 
                Message response = channel.EndRequest(asyncResult);
                CalculatorServiceResponse oCalcResp = response.GetBody<CalculatorServiceResponse>();
                String strOutput = "Result of "+ oCalcResp.Method +"=" + oCalcResp.ReturnValue;

                await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new Windows.UI.Core.DispatchedHandler(() =>
                {
                    txtOutput.Text = strOutput;
                }));
            }
            catch (Exception oEx)
            {
                // handle the exception
                this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new Windows.UI.Core.DispatchedHandler(() =>
                {
                    txtOutput.Text = "There was an exception: " + oEx.Message;
                })).AsTask().Wait();
            }
        }

9.)    The other two functions that are present in the CustomCalculatorClient.cs file – GetStatusCode and GetStatusDescription only extract the HTTP response code and the status description of the HTTP response.

10.)    MainPage.xaml has 4 buttons that handle each of the 4 operations - Add, Subtract, Multiply and Divide, 2 textboxes that accept the input parameters and a TextBlock that displays the output. 

11.) The click of the Add button has the following implementation: 

         private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Calculator oCalc = new Calculator();
                CustomCalculatorClient oCalcClient = new CustomCalculatorClient(serviceLocation);
                oCalc.Method = "Add";
                oCalc.d1 = double.Parse(txtInput1.Text);
                oCalc.d2 = double.Parse(txtInput2.Text);

                IAsyncResult result = oCalcClient.PostAsync(serviceLocation, oCalc, RespCallback, oCalcClient.InnerChannel);
            }
            catch (Exception oEx)
            {
                txtOutput.Text = "Add exception: " + oEx.Message;
            }
        }

The above function creates an instance of the Calculator class – that populates the requested method (Add) and then assigns the input parameters: d1 and d2.  

An instance of the CustomCalculatorClient class is then created where we pass in the location of the remote service – serviceLocation that is set to https://localhost:8001/CalculatorService.  

Note: The server address here is set to localhost and is for demonstration purposes only. For an actual Windows Store app, you cannot use localhost as the server address.

Next the PostAsync method is called, which will be responsible for sending the HTTP request asynchronously to the Service. The response processing happens in the RespCallback function as described in Step 7 that will extract the response body and display the results to the user.

To run the project, compile the solution and start an elevated command prompt from where you will start the POXMessagingService service. By default, the service starts listening on the https://localhost:8001/CalculatorService URI location. If you have other applications listening on port 8001, please change the port references to another port in both the projects. Alternately, start Visual Studio as an Administrator and then set the Startup Project to the POXMessagingService project (Right click on the POXMessagingService Project, and then select "Set as Startup Project").

 

Once the service is started, you can start your POXMessagingClient Windows Store app and then use the Add, Subtract, Multiply, Divide operations on the remote service.

 

This shows how you can consume a WebService using simple XML from a Windows Store app.

 

Follow us on Twitter @wsdevsol. 

POXMessaging.zip