None

Consuming Web Service with C#

February 2, 2010

I had a need to test a web service from C#, as that would be what many of our customers would be doing. I had no experience of C# or Visual Studio. This post recounts the trials and tribulations of getting this to work.

I'm using Visual C# Studio 2008 Express, though some of the technology used is for the 2005 version as I could call on some knowledge of this version from colleagues.

Creating the Web Reference

Create a new console application within visual studio. In Studio 2005, use the Project/Add Web Reference menu option. This doesn't exist in Studio 2008, the way around this is to use Project/Add Service Reference, then click Advanced, the use the Add Web Reference button.

In this dialog, enter the path to the WSDL. In my case I was using a local file, but it is possible to refer to remote WSDLs at URLs too. Closing this dialog will generate some code and add a Web Reference to your project.

In this example, I've used the name WebReference for the web reference.

The Web Reference in Code

At this point, we can refer to the web reference in code:

WebReference.Provide_Process_LeadService lService = new WebReference.Provide_Process_LeadService();
lService.Provide_Process_Lead(lApplication);

This corresponds to the following service in the WSDL:

<wsdl:service name="Provide_Process_Lead">
    <wsdl:port name="Provide_Process_Lead_Port" binding="tns:Provide_Process_Lead_Binding">
        <soap:address location="http://localhost/Process_Lead"/>
    </wsdl:port>
</wsdl:service>

Parameters

In the above code, you'll notice lApplication being passed into the web service call. This is an instance of an object defined in the WSDL, in this case of type Application. You can create this in your code using:

WebReference.Application lApplication = new WebReference.Application();

Application has an attribute called Agreement, of type Agreement. Again, we can create this using the new operator:

lApplication.Agreement = new WebReference.ApplicationAgreement();

I had problems at this point, because it complained that the Agreement attribute was read only. This could be confirmed by looking at the generated code which only had getter methods not setters. This was due to some entries in the schema I was using that had found their way into the WSDL:

 <xs:element msdata:EnforceConstraints="False" msdata:IsDataSet="true" msdata:Locale="en-GB" name="Application">

Changing this part of the WSDL to be

<xs:element  name="Application">

(ie removing the microsoft specific bits) made this problem go away, and the Agreement attribute became read/write.

Proxy Server

I needed to do my call using a proxy server, and the code didn't pick up the details from my machine. I had to specify them manually.

WebReference.Provide_Process_LeadService lService = new WebReference.Provide_Process_LeadService();

IWebProxy lProxy = new WebProxy("proxy.server.com:1234", true);
lProxy.Credentials = new NetworkCredential("username", "password");
lService.Proxy = lProxy;
lService.Provide_Process_Lead(lApplication);

I also had to add using System.NET to be able to use the classes in here.

Final Code

The final code used to do the call looked something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            WebReference.Application lApplication = new WebReference.Application();
            lApplication.Agreement = new WebReference.ApplicationAgreement();
            lApplication.Agreement.advance = "5000";
            lApplication.Agreement.loan_purpose = "DEBT CONSOLIDATION";

            lApplication.Applicants = new WebReference.ApplicationApplicant[1];
            lApplication.Applicants[0] = new WebReference.ApplicationApplicant();
            lApplication.Applicants[0].date_of_birth = "20/01/1961";
            lApplication.Applicants[0].title = "Mr";
            lApplication.Applicants[0].surname = "Bloggs";
            lApplication.Applicants[0].forename = "Fred";
            lApplication.Applicants[0].employment_status = "Employed";

            WebReference.Provide_Process_LeadService lService = new WebReference.Provide_Process_LeadService();

            IWebProxy lProxy = new WebProxy("proxy.server.com:1234", true);
            lProxy.Credentials = new NetworkCredential("username", "password");
            lService.Proxy = lProxy;
            lService.Provide_Process_Lead(lApplication);
        }
    }
}

HTTPS

Getting this to work with a HTTPS reference rather than HTTP was trivial. All I had to do was update the WSDL file to have the new URL in it, and then right click the Web Reference and select Update Web Reference. This pulls in the latest WSDL and regenerates the code.

Outstanding Problems

For some reason all of the generated attributes of type decimal (ie defined in the XSD as xs:decimal) weren't being passed through from the C# side to the Cast Iron side.

For the purposes of my test, I changed the type to xs:string and updated the web reference. These values were then passed through correctly. I'm not sure exactly what was going wrong here. Regardless of how I created the decimal (5000.0M, new decimal(5000), new Decimal(5000)) the value wasn't being passed through. Clues appreciated in the comments!