Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / .NET / .NET4

A Simple Sample: WCF Service

4.65/5 (30 votes)
1 Nov 201011 min read 484.4K   51.3K  
Writing, configuring, and consuming a WCF Service using VS2010

Introduction

This article discusses the simplest way to write, configure and consume Windows Communication Foundation (WCF) service, using Visual Studio 2010. This would help gain a better understanding to WCF services which is slightly different from ASP.NET web services.

Background

We would look into writing and consuming a simple service using VS2010.
Scenario: Book store service that would fetch the book information.

At the end of the article, you would know:

  • How to build a WCF service
  • How to consume WCF service in Windows Forms
  • How to bind custom object with DataGridView
  • How to configure WCF Service while publishing
  • How to convert XElement to custom object using LINQ

Also, you might be interested in Why use MessageContract when DataContract is there?; an article that I wrote some time back. BTW, we will use both in this example.

Steps to Follow

Let's create a WCF Service Library project. Visual Studio 2010 stubs-in a default service which it calls Service1. Let's ignore this existing service for a while now. We would create a separate service that would return the list of books requested by the client end.

Note that to just to keep things simple, we would use XML file as our data store; taken from MSDN.

The XML has columns: Author, Title, Genre, Price, Publish Date, Description, and Book ID.

The Book ID, which is a string shall be used as primary key to identify the book.

We would add a book interface that shall define what this service provides as book service. So, we want to provide a service that returns the list of books found based upon user criteria.

Add a new item as interface called IBookService under namespace Store. Add the directive, using System.ServiceModel;
Decorate the interface with service contract attribute as [ServiceContract].

We want the following functionality as a scope of this sample:

  • List of all books
  • Filter functionality; return a book or a list of books given its ID or Title or Genre or Author

Note that we will also look into the .NET default/optional arguments functionality that is provided in C# v4.0, as a part of this sample while we implement the above methods.

The interface shall contain the methods.

Let’s define the operations for IBookService interface:

C#
namespace Store
{
    [ServiceContract]
    interface IBookService
    {
        [OperationContract]
        List GetAllBooks();//Get all books; returns list of books

        [OperationContract]
        List GetBookByID(string BookID);//Gets a(single) book by ID

        [OperationContract]
        List Filter(string Author, string Genre, string Title); //Returns list of 
						//books by specified filter
    }
} 

Let's add a Book type and define the attributes of the book that we want for the client to have. For now, it's all those attributes that are there in the XML data.

Right click on the Book return type, and select Generate Class for Book. This shall generate the class of type Book. Note that you also write the attributes where it is to be used and VS shall add those attributes in the class automatically.

fig1.jpg

FIG 1: Book service interface

If you select the generate new type, it will show the following window and provide you with the options about class. Its Access specifier, Kind (class, struct, etc.), and either to create a new file and stub the code in the current file. We would select a separate file.

fig2.jpg

FIG 2: Add Book data type

Right click on the Book return type and select Goto Definition.

Add the directive using ServiceModel, and using System.Runtime.Serialization;. And DataContract attribute on Book class; it would look like the following:

fig3.jpg

FIG 3: List of book attributes, similar to XML element attributes.

In figure 3, note that the ID is of type string, ideally IDs should be of integer type, when using as primary keys, integers keys work faster than the string keys. The reason we are using the string type primary key is that we have string data in the XML data store.

Let's decorate the Book class with DataContract attribute.

A data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged. That is, to communicate, the client and the service do not have to share the same types, only the same data contracts. A data contract precisely defines, for each parameter or return type, what data is serialized (turned into XML) to be exchanged.

Windows Communication Foundation (WCF) uses a serialization engine called the Data Contract Serializer by default to serialize and deserialize data (convert it to and from XML). All .NET Framework primitive types, such as integers and strings, as well as certain types treated as primitives, such as DateTime and XmlElement, can be serialized with no other preparation and are considered as having default data contracts.

Let's add the types that are required ID, Title, Author, Description, Genre, Price, and Publish Date and tag all members with [DataMember] attribute.

Now, we will add a class BookService that implements the IBookService interface;

The book service shall contain the definition.

fig4.jpg

FIG 4: Implement book service interface

Note, in case if you plan on using the database (and not the XML which is a part of this example), you can use Enterprise Library, Data Application Block for your data transactions; you will need to add a reference to the Data.dll file, generally available in the DRIVE:\Program Files\Microsoft Enterprise Library 4.1 - October 2008\Bin.

Now let's add the implementation of the methods. First, there this is a small GetDb() method, that loads the data from given XML and into the XDocument object.

Then, since we interested in the book nodes, therefore we select all the books.

The select new Book() creates a new object and copies the data from book attribute into our defined book object attribute. So by the end of the book structure "}" is complete, we have our book object ready to be inserted into the List object.

Implementing both the methods using XDocument and LINQ, answers the question, how to convert XElement to custom object using LINQ.

C#
public List GetAllBooks()
{
    XDocument db = GetDb();
    List lstBooks
         =
        (from book in db.Descendants("book")
         select new Book()
         {
             ID = book.Attribute("id").Value 	//Get attribute from XML and 
					//set into the Book object attribute.
             ,
             Author = book.Element("author").Value
             ,
             Genre = book.Element("genre").Value
             ,
             Price = Convert.ToDecimal(book.Element("price").Value)
             ,
             Description = book.Element("description").Value
             ,
             PublishDate = Convert.ToDateTime(book.Element("publish_date").Value)
             ,
             Title = book.Element("title").Value
         }).ToList(); //Cast it into the list

    return lstBooks;
}

The above is the method that gets all of the books in the datastore. Now, let's add the definition for GetBookByID(). The method is the same as get all books, except for the where clause. Note that this shall be only one book in this case, so the list shall contain only one item.

C#
public List GetBookByID(string BookID)
{
    XDocument db = GetDb();

    //Howto: Convert XElements to Custom Object
    List lstBooks =
        (from book in db.Descendants("book")
         where book.Attribute("id").Value.Equals(BookID)
         select new Book() //Instantiate a new object 
         {
             ID = book.Attribute("id").Value
            ,
             Author = book.Element("author").Value
            ,
             Genre = book.Element("genre").Value
            ,
             Price = Convert.ToDecimal(book.Element("price").Value)
            ,
             Description = book.Element("description").Value
            ,
             PublishDate = Convert.ToDateTime(book.Element("publish_date").Value)
            ,
             Title = book.Element("title").Value
         }).ToList();


    return lstBooks;
}

The above code gets a book given its ID using LINQ.

Configuration and Deployment

Add the service definition in app.config file under system.serviceModel/services tag.

The system.serviceModel/services tag contains the classes, enumerations, and interfaces necessary to build service and client applications that can be used to build widely distributed applications.

XML
<service name="Store.BookService">
        <endpoint binding="basicHttpBinding" contract="Store.IBookService"></endpoint>
      </service>

basicHttpBinding represents a binding that a service can use to configure and expose endpoints that are able to communicate with ASMX-based Web services and clients and other services that conform to the WS-I Basic Profile 1.1 [^] . Contract is the name of the interface that we expose.

Note that a WCF service requires an application host, in order to run and be accessible to clients.

We have a couple of options here, for instance:

  • Create a custom host application
  • Build a Windows service application
  • Using IIS

In our case, we would use IIS to simply publish the service.

Right click on the project and select Publish, would generate the following directory structure in IIS.

fig5.jpg

Fig 5: Configure WCF Service in IIS

Just for your interest, if you are using a version prior to VS2010 to configure a WCF service, following is the manual process:

  • Ensure that you have the binary files being built inside the \bin folder, rather than \debug or \release folder.
  • Add a service definition file, a file having .svc extension.
  • Add a new item, select the Text File template; rename the file to BookService.Svc. This shall contain the service definitions.

Fortunately, Visual Studio 2010 does that for us.

We also need to tell the IIS that our service is going to use the .NET Framework version 4.0, so that it does not use its default .NET framwork.

Fig 6 shows how to change the framework that IIS is going to use for our app.

fig6.jpg

Fig 6: Change app's framework in IIS

Publish in IIS

In order to be able to be accessible to the outside world, we need to allow access. You can open the URL in IE and see it works. In my case, for instance, I have it under WCF folder http://localhost/WCF/Store.BookService.svc.

fig7.jpg

FIG 7: Open service URL in the browser

Note the highlighted text in the above image. This requires a service behavior to be added in the config file, which Visual Studio 2010 stubs in for us automatically.

You will need to set the httpGetEnabled attribute to true, in order to publish your service metadata. It's a Boolean value that specifies whether to publish service metadata for retrieval using an HTTP/Get request. The default is false.

To save and publish the service into IIS, click on Save, Publish.

Now you can open the URL again in Internet Explorer, and you should be able to see your service's meta.

fig8.jpg

Fig 8: Book service in Internet Explorer

Quick way to see the wsdl, type ?wsdl in the address bar to see the wsdl, like: <a href="http://localhost/WCF/Store.BookService.svc?wsdl">http://localhost/WCF/Store.BookService.svc?wsdl</a>.

fig9.jpg

Fig 9: Book service WSDL listing

How to Consume WCF Service?

We would create a small forms based client app that would show a couple of filter options, and provide a search button that requests the service for books based upon the filter provided by the user.

Add a Windows Forms project and design the form.

fig10.jpg

Fig 10: Client user interface (UI)

To be able to consume the service, we will need to add a reference to that service. So when you try to add the service reference, the IDE discovers all the services on your system. Alternatively, you can provide the path that you have of the service.

Note that web services, by nature, are of public type. Though, WCF adds the Service, Message, and Data level contracts; but the service itself is public.

So right click on the WCF Client project and add a service reference. In your client app, add the service reference.

Add following as the service reference URI: http://localhost/WCF/Store.BookService.svc?wsdl. I would rename the reference to SvcBookstore.

fig11.jpg

Fig 11: Client user interface (UI)

Since, at this point, we have already added the service reference, therefore we can access it by adding the using directive in the forms class, and then declaring the object of the service; exactly similar to how we add/declare other .NET objects. Let's declare the service object in our form. And add using directives.

One important thing: What if you are in the middle of developing real world WCF Services, and now you want to test it. And while testing using a demo client app, your service is throwing exception that you have hell no idea of. So in that case, a "natural" scenario a developer wants is that you should able to "step into (F11)" the service code and see if for yourself. That is going be to a great help. So, if this is the case, you can always go back to your service configuration file and add a serviceDebug within behavior element.

XML
<servicedebug includeexceptiondetailinfaults="True" />

serviceDebug allows the client app to receive exception details in faults for debugging purposes, when set to true. DO NOT forget to set to false before deployment to avoid disclosing exception information.

So, if you want to get the service related exception here at the client end, add a tag in service. Because, at this point, you might want to step into it.

Client Code

So, let's add the final code that collects the filter specified by the user, and call the service. When the data is retrieved, you can simply just assign object array to .DataSource property to show on Grid. Following the output of the client.

C#
private void button1_Click(object sender, EventArgs e)
{
    //Get the combo choice, if there is any.
    string strGenre = cbxGenre.SelectedIndex > -1 ? 
		cbxGenre.SelectedItem.ToString() : string.Empty;

    //Declare the books array; though the actual return type is List<books />, 
    //it actually gets casted into
    //Book[] array.
    Book[] lstBooks = null;

    //Discard other filters, if user has entered a book id
    if (!string.IsNullOrEmpty(txtID.Text))
    {
        lstBooks = bookService.GetBookByID(txtID.Text);
    }
    else
    {
        //Lets get books by filter.
        lstBooks = bookService.Filter(Author: txtAuthor.Text, 
			Title: TxtTitle.Text, Genre: strGenre);
    }

    //Set datasource, custom object.
    dataGridView1.DataSource = lstBooks;

}

Did you notice bookService.Filter(Author: txtAuthor.Text, Title: TxtTitle.Text, Genre: strGenre); line in the code above?

That's what the named arguments are. Named arguments free you from the need to remember or to look up the order of parameters in the parameter lists of called methods. The parameter for each argument can be specified by parameter name. Named arguments can follow positional arguments.

As an example, if at some point, for this client app, user only provides the Author to look for, you can provide only the author to this method. Like: bookService.Filter(Author: "Paulo Coelho");

Note that you will have to update the Service's .Filter() method and provide the default arguments; for instance, like the following:

C#
public List<book> Filter(string Author = "N/A", string Genre = "N/A", 
		string Title = "N/A")

fig12.jpg

FIG 12:Output of client user interface (UI)

Points of Interest

Couple of things that I noticed is that WCF service is way different than an ASP.NET service. The file extensions are different as well. .asmx for ASP.NET and .svc for WCF. Also, after all that you have done above, and deployed your application and now you are wondering, WHY your service is taking time on the first load. Well, Why my service is slow on first access? might just help you.

Enjoy!

History

  • 1st November, 2010: Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here