How to Consume a Web Service

This question comes up more than you would imagine. Although it’s a simple question, it’s hard to find a simple answer. This post will attempt to be the simple answer. Any complications will be added as other posts, referencing this one.

Creating the Test Service

I’ll just start by creating a simple WCF service for us to consume later. This will also create the Visual Studio Solution we’re going to use. Start with File->New Project:

This brings up the New Project dialog. I’ll choose the “WCF Service Application” project template, just for simplicity. In general I would have chosen “WCF Service Library” from the WCF page.

 

This results in the following solution structure:

Actually, Visual Studio has provided all we need for this example. It created a Data Contract class for our use:

[DataContract]
public class CompositeType
{
    [DataMember]
    public bool BoolValue { get; set; }

    [DataMember]
    public string StringValue { get; set; }
}

It also created a service contract that our service will implement. This contract uses the Data Contract:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);
}

It also created the service as a class that implements the service contract:

public class Service1 : IService1
{
    public string GetData(int value)
    {
        return string.Format("You entered: {0}", value);
    }

    public CompositeType GetDataUsingDataContract(CompositeType composite)
    {
        if (composite.BoolValue)
        {
            composite.StringValue += "Suffix";
        }
        return composite;
    }
}

It also created the Service1.svc file that will tell IIS (or the Visual Studio Development Server) which class to create to handle calls to the server:

<%@ ServiceHost Language="C#" Debug="true" Service="WcfService1.Service1" CodeBehind="Service1.svc.cs" %>

When a request for Service1.svc arrives at the URL the service is on, IIS will create an instance of the  System.ServiceModel.ServiceHost class to host instances of the the WcfService1.Service1 class. As requests for this service arrive, they will be dispatched to instances of the WcfService1.Service1 class.

The web.config has also been created with the default configuration necessary for this service.

Creating a Simple Client

There is no real difference between consuming a web service in a Console application as opposed to an ASP.NET application or a Windows Forms application, so I’ll just use a console application:

This produces:

We start by adding a Service Reference to the project. Right-click the ConsoleApplication1 project and choose “Add Service Reference”:

The value you place into the “Namespace” text box at the bottom is important, so I often fill it out first, so I don’t forget. Note here that I have changed the default of “ServiceReference1” to “Service1Reference”, to indicate that this will be a reference to “Service1”.

The “Address” text box at the top can be used to type the URL to the Service Description, or “WSDL” file. Since the service is in the same solution as the project we’re trying to add it to, we can take advantage of the “Discover” dropdown at the right. Clicking the arrow shows “Services in this solution”:

Clicking this will populate the “Address” box with the URL to our service, and will populate the “Services” list with the “Service1” service:

Now, a service may implement more than one service contract. You should click the “+” sign next to the service to expand the service down to the service contracts and their operations. Unfortunately, doing so now will illustrate that I’ve made a common error. After attempting to launch our service, the text below the service list will change to say “An error (Details) occurred while attempting to find services at http://localhost:50730/Service1.svc” Clicking the Details link will show:

Note that this is a bunch of HTML! In fact, there’s so much of it, that there is a scroll bar to the right. if you scroll to the very bottom, you’ll see the reason for this problem, and the solution to it:

Yes, I forgot to build the service! After successfully building the service and starting over, you’ll be able to expand the service all the way to the bottom, and see that both methods are visible:

Visual Studio will add several project references to your project, and will add the service reference:

Note that it also added an app.config. This will contain the configuration details necessary for the client to communicate with the service. These will be the defaults, and can be modified as required.

It’s usually at this point where someone new to web services stops to ask: “Now, what?”

What’s In a Service Reference?

A service reference is a convenient way for you to access a web service in your code. It takes all the complexities of web services and boils them down into a single name, in this case “Service1Reference”. But in order to see “what’s in a name”, all we have to do is right-click the service reference and choose “View in Object Browser”. The first thing it shows is actually interesting, so stop and look:

You’ll see that Visual Studio has created a new namespace under the namespace for this application. Any names brought over from the service cannot interfere with names in your application, because those names are all created in a separate namespace.

Expanding the namespace shows what’s inside:

Visual Studio has created a “CompositeType” class, since the service contract uses a class with that name. It has also created an IService1 interface to represent the service contract, as well as a Service1Client class that we’ll use shortly. If you click on IService1, you’ll notice that it has the expected GetData and GetDataUsingDataContract methods. If you expand Service1Client to see the base classes, you’ll see that it implements IService1, so that it contains implementations of the GetData and GetDataUsingDataContract methods.

This all makes it sound more difficult than it is. Let’s write some code instead! First, let’s get the data we’ll send to the service:

Console.Write("Enter BoolValue: ");
var line = Console.ReadLine();
if (line == null)
{
    return;
}

var boolValue = bool.Parse(line);

Console.Write("Enter StringValue: ");
line = Console.ReadLine();
if (line == null)
{
    return;
}

This is just simple-minded logic for reading in the two values we need to construct a CompositeType.

using (var svc = new Service1Client())
{
    var request = new CompositeType
                      {
                          BoolValue = boolValue,
                          StringValue = line
                      };
    var result = svc.GetDataUsingDataContract(request);

This is straightforward. Just create an instance of the Service1Client proxy class, create an instance of CompositeType , and call the method, passing the request and receiving the response. The response can be displayed in a simple manner:

Console.WriteLine("The result is:");
Console.WriteLine(
    "BoolValue={0}, StringValue={1}", 
    result.BoolValue,
    result.StringValue);
Console.Write("ENTER to continue:");
Console.ReadLine();

After that, we’re through. This can be tested by using CTRL+F5 to run the code without debugging. The result is:

But What About…

There’s one little problem in the code above. In every case, when your code creates an instance of a class that implements IDisposable, then your class should ensure that Dispose gets called on that instance. In most cases, the way to do that is shown above: create the instance within a using statement. Unfortunately, the designers of WCF made a mistake that causes the use of WCF proxy classes to be the one case where this doesn’t work. See Indisposable – WCF Gotcha 1 (7/3/2008) for the details, but we’re going to have to change our code a little. Instead of:

using (var svc = new Service1Client())

{

    // Do something with svc

}

we have to do this:

Service1Client svc = null;
bool success = false;
try
{
     svc = new Service1Client();
     
     var request = new CompositeType
     {
         BoolValue = boolValue,
         StringValue = line
     };
     var result = svc.GetDataUsingDataContract(request);

     Console.WriteLine("The result is:");
     Console.WriteLine(
         "BoolValue={0}, StringValue={1}",
         result.BoolValue,
         result.StringValue);
     Console.Write("ENTER to continue:");
     Console.ReadLine();

    svc.Close();
    success = true;
}
finally
{
    if (!success && svc != null)
    {
        svc.Abort();
    }
}

The highlighted code is the only difference. To make a long story short, if you don’t follow this pattern, and if an exception is thrown inside of your using block, then you may lose your exception. The exception will attempt to leave the using block, the block will call Dispose for you, and then Dispose may throw an exception of it’s own, losing you any information about what really went wrong in your using block. In the code above, if Close is never called, then Abort will be called, and it’s abort that will do the cleanup.

Don’t let this keep you from the using block in every other case. It is only because the WCF designers decided to have Dispose call Close, and since Close can throw its own exception, that this is an issue. As far as I know, there is nowhere else in .NET where calling Dispose can throw an exception.

About these ads
This entry was posted in Web Services. Bookmark the permalink.

14 Responses to How to Consume a Web Service

  1. Marcos says:

    Your "What about…" section is something we encountered a couple of months ago. I was very surprised to see the using statement causing this type of error. We have been going through our app making changes similar to your example. Very good tip.

  2. John says:

    Actually, you are now in the same place I learned about this. I had at least part of a solution in an extension method meant to be used in the clients. Unfortunately, I was laid off before I could finish implementing the changes.

  3. hari says:

    i get an error

    using (var svc = new Service1Client())
    {
    var request = new CompositeType

    It could not find CompositeType and Service1Client . But, i can see them in Objectbrowser. I followed all your steps exactly. what could be the problem? I am new to WCF . so, please help . Thanks

    • guest says:

      hari, add a using statement to reference the “Service1Reference” namespace.
      using ConsoleApplication1.Service1Reference;

  4. abdelali Iferden says:

    thanks

  5. pete says:

    You mention that you can add a service reference using a WSDL file. This is what I’m trying to do, however the WSDL file contains URL references to the service itself, ending with querystrings like: ?wsdl=wsdl0 or ?xsd=xsd0. These cannot be resolved when adding the Service Reference in a client solution because the service is not reachable from my development box. This seems like more of a service-side issue because we want it’s WSDL to stop containing these references to the service. Have you encountered this problem when adding a Service Ref to a service that is unreachable from your development box? How would you solve this?

    • Pete, I would have them send you all of the constituent files. I would then try using “Add Service Reference” and pointing to the location on disk. If that doesn’t work by itself, you might need to rearrange the files if some of the links assume a particular folder structure. Finally, it might be necessary to edit the files to remove the links.

      svcutil.exe is the command-line equivalent of “Add Service Reference”. You should try pointing it at the WSDL on disk, and all of the other files.

  6. Flash says:

    I am getting the following error while trying to consume a webservice that requires asymetric encryption using VS 2010: WSDoAllReceiver: Request does not contain required Security
    header.

    This is my first web services consumption. I am using a session X.509 certificate to sign the message, then generate a session key to encrypt the message. Finally I use the X.509 certificate to encrypt the key, attach the key to the message, and then use the instance of the webservice to send the message. I followed the direction here: http://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.encryptedxml.aspx

    On the webservice instance I add the following:
    MyWebServiceInstance.ClientCredentials.UserName.UserName = “assignedUserID”
    MyWebServiceInstance.ClientCredentials.UserName.Password = “assignedUserPassword”

    What do I need to do to add the required security header?

  7. Quang says:

    “Now, a service may implement more than one service contract. You should click the “+” sign next to the service to expand the service down to the service contracts and their operations. Unfortunately, doing so now will illustrate that I’ve made a common error. After attempting to launch our service, the text below the service list will change to say “An error (Details) occurred while attempting to find services at http://localhost:50730/Service1.svc” Clicking the Details link”

    how to build the service?

  8. bg says:

    Thanks. The illustrated steps help me get thru a Console App using a vendor supplied WSI. Much appreciated.

  9. xgrinderx says:

    Great article! Excellent info for a person like me who is relatively new to web services. The problem I am presented with is – the app I am working on stores web service locations, usernames, and passwords in a database. So it is not a static thing that I can simply add a reference to – is there some way to use this simple method to reference the database entries to add the service reference?

    • Sorry I didn’t see this sooner.

      If all of the services you’re referencing have the same contract, then you can add a single Service Reference, and just set the URL from the database.

      To be honest, if the services are all different, then I have a hard time imagining how you can treat all of them uniformly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s