Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

WCF Service Library with Windows Service Hosting

4.86/5 (94 votes)
15 Jul 2009CPOL11 min read 333.5K  
Using this application as sample anybody can design their applications in n-tier model using WCF

Introduction

Usually we prepare WCF services and host them in IIS which is useful when there are different clients accessing the service from outside the system. But if the service is intended for local system clients, then it would be a better idea to host the WCF service as a Windows service. In this article we see how to create a WCF service and make it run as a Windows service.

Using this application as sample anybody can design their applications in n-tier model using WCF.

For this we follow a tiered architecture, where the service resides at one location, hosted at another and consumed from another location.

To have a clear understanding about what we are going to do, here is the agenda:

  1. Creating a WCF Service library which packs our service in a dll file
  2. Testing the created service using the Test Host environment
  3. Consuming the service from that environment
  4. Registering the service dll with Windows OS to make it run as a Windows service
  5. Consuming the WCF service which is running as a Windows service

As our main intention is to show how to follow these steps, we are preparing a really simple service to make things less complicated. This service takes the JobId of Jobs table of default pubs database and in result gives the Job description and some other data .

So, without much ado we start building our applications.

Creating WCF Service Library

i. Creating a new WCF Service Library and adding our service

The WCF Service Library template is present under WCF project type while creating a new project in Visual Studio.

So, create a New Project ->navigate to WCF option -> select WCF Service Library and give some name. Here we are naming it as WCFJobsLibrary.

Image 1

This will create a service library with a default service interface and a class to implement it. As we don’t need them, we simply delete them and create a service with our own custom name. For this, in Solution Explorer, right click on the project, select Add -> New Item and choose WCF Service. We name it as Jobs.

Image 2

This step will create an IJobs interface and Jobs class. Also, the service configuration for Jobs will be automatically written in App.Config by Visual Studio. You can see this if you scroll down to <services> tag for end point details and <behaviors> tag for service behaviour under <system.serviceModel>.

Image 3

By default, two endpoints will be created- one which uses wsHttpBinding and a mex endpoint with mexHttpBinding. The mex endpoint provides metadata to the client for creating a proxy, the process which we’ll discuss later.

ii. Preparing our own complex type and making it serializable & WCF-enabled

Right now, we’ll proceed to write the code for our service. In Jobs class we are going to define a method which returns a complex type with Job details. For this complex type we should prepare a class with all the required fields. So, add a new class from Add -> New Item and name it as Job.cs.

The class must be specified by [DataContract] attribute to make it serializable and WCF-enabled. Similarly the properties should be specified with [DataMember] attribute. As these attributes are present in System.Runtime.Serialization namespace which is not available in the class by default, we need to import that namespace.

C#
using System.Runtime.Serialization;
namespace WCFJobsLibrary
{
    [DataContract]
    public class Job
    {
        [DataMember]
        public string Description { get; set; }
        [DataMember]
        public int MinLevel{get;set;}
        [DataMember]
        public int MaxLevel { get; set; }
 
    }
}

iii. Adding Contracts

Now, our next step is to add the contract. For this, in IJobs interface we specify our methods and decorate them with [OperationContract] attribute.

C#
[ServiceContract]
public interface IJobs
{
    [OperationContract]
    DataSet GetJobs();

    [OperationContract]
    Job GetJobInfo(int Jobid);
}

Here the two methods perform nearly the same action, with a slight difference that GetJobs() method will fetch all the records in the Jobs table, whereas GetJobInfo() will fetch only the record matching the given Jobid.

GetJobs() returns a DataSet which is an in-built complex type, whereas GetJobInfo will return Job type which we have already defined in our Job class.

iv. Implementing the Interface

We implement the IJobs interface in Jobs class. For this we go to Jobs class and right click on IJobs and select Implement Interface option to implement IJobs interface automatically in Jobs class. When this is done, we get empty methods where we have to write our code. The code for the methods is written as follows:

C#
public class Jobs : IJobs
{
   #region IJobs Members
 
   public System.Data.DataSet GetJobs()
   {
       SqlConnection cn = new SqlConnection("data source=mytoy; user
                                             id=sa; database=pubs");
       DataSet ds = new DataSet();
       SqlDataAdapter da = new SqlDataAdapter("Select * from jobs", cn);
       da.Fill(ds, "jobs");
       return ds;
   }
 
   public Job GetJobInfo(int Jobid)
   {
       SqlConnection cn = new SqlConnection("data source=mytoy; user id=sa;
                                             database=pubs");
       cn.Open();
       string sqlstat = "select * from jobs where job_id=" + Jobid;
       SqlCommand cmd = new SqlCommand(sqlstat, cn);
       SqlDataReader dr=cmd.ExecuteReader();
       Job jobObj=new Job();
       if(dr.Read())
       {
           jobObj.Description=dr["job_desc"].ToString();
           jobObj.MinLevel=Convert.ToInt32(dr["min_lvl"].ToString();
             jobObj.MaxLevel=Convert.ToInt32(dr["max_lvl"].ToString();
       }
       else
             jobObj.Description="-1";
       return jobObj;
    }
 
    #endregion
}

There’s nothing much to discuss in this code, which performs the task of retrieving required data from the database. For clarity, all the connection strings and statements are hard-coded here, but you can do it in a standard way. The main thing to note here is that these two methods are getting data from database and returning it in complex types.

With this our service is ready. To pack it in an assembly we should build the project. After successfully building, we’ll get a dll in bin folder of project directory which is WCFJobsLibrary.dll.

Testing the WCF Service

After the library is built, run the assembly. This will host our service in the test environment. A new icon appears in task bar which shows that the service is hosted.

Image 4

Also a test client window is opened which gives the status information that the service is successfully added.

Image 5

Image 6

The service client has the information of the address and binding that is used. It even has the info of the methods that are available for consumption. In test environment, some methods may not be supported, which is because some complex types are not supported by the test client. For whichever methods are not supported, an exclamation mark appears beside the name of that method as shown in above screenshot for GetJobs().

Nevertheless, we can test the other method GetJobInfo(). Just click on that service method in test client window and it will show the service info at the right side of the window. This info is divided into Request and Response parts. In Request the parameter names, their values and types are listed. Similarly in Response the info of return types are given. When we enter a parameter value in Request and press the invoke button, it will give the result in Response as shown in screenshot below.

Image 7

But if the test client window is closed, the service is also removed from the host environment. To make the service independent of this test client, we have to detach the test client from the project. To do this go to WCFJobsLibrary properties and move to the Debug tab. Here we’ll find the test client name in command line arguments box. Removing the client name from that box will detach the client from the service.

Image 8

Consuming the WCF Service

As we have successfully tested the service, our next step is to consume it. For this we create a windows app as our client.

i. Design the Form

Create New > Project > Windows Forms App and name it as ConsumeJobs.

We design the form as shown below with a textbox to provide the JobId, a button to invoke the sevice and a few more textboxes to display the results. Similarly we use a DataGridView and another button to display all the records in Jobs table.

Image 9

ii. Creating Proxy

To communicate with the service, the client should have a proxy.

For creating a proxy the client needs the reference of WCF service.

For adding reference go to Solution Explorer and right click the project (client app) and choose Add Service Reference. A window opens where we have to give the address of the service. We can copy this address from the base address in app.config file of our WCF Service Library. If the communication between the client and server is through http, then we can use that address as it is, otherwise if it is through any other channel, then we need to add ‘/mex’ at the end of the address.

For example if the base address is :

http://localhost:8731/Design_Time_Addresses/WCFJobsLibrary/Jobs/

then for any channels other than http we have to add ‘/mex’ at the end like this:

http://localhost:8731/Design_Time_Addresses/WCFJobsLibrary/Jobs/mex

After providing the address and clicking the Go button, the services available at that address are found and also a list of interfaces at that services are shown. It should be remembered that this step will be successful only if the service is running.

Select the service and provide a meaningful name for the namespace(alternately you can use the default name as well). In our case we provide the name as JobsService. When the ok button is clicked, a proxy will be created at the client with a similar name as the original class at the service, but suffixed by the word ‘client’. In the WCF Service library as our class name is Jobs, at the client side the proxy name will be JobsClient.

Image 10

iii. Writing Code to Consume Service

As the proxy class is created we can proceed to write the code in button click events of the client Windows form.

C#
private void btnViewDetails_Click(object sender, EventArgs e)
{
    //an instance of the proxy class
    JobsService.JobsClient obj = new ConsumeJobs.JobsService.JobsClient();
 
    //Reading the result into complex type object (Job type)
    JobsService.Job jobObj = obj.GetJobInfo(int.Parse(txtJobId.Text));
 
    //Displaying result in text boxes
    txtDescription.Text = jobObj.Description;
    txtMinValue.Text = jobObj.MinLevel.ToString();
    txtMaxValue.Text = jobObj.MaxLevel.ToString();
           
 }
 
 private void btnShow_Click(object sender, EventArgs e)
 {
    JobsService.JobsClient obj = new ConsumeJobs.JobsService.JobsClient();
    DataSet ds = obj.GetJobs();
    dataGridView1.DataSource = ds.Tables[0];
 }

We get the result from the database which is retrieved by the service program and which is in turn accessed by a proxy class at the client.

Image 11

Till now we have seen about consuming the service when it is hosted in the hosting environment provided by Visual Studio. Now we’ll see how to register this service with Windows so that it runs as a Windows service.

Creating Windows Service for Hosting the Service DLL

For this, first we have to create a Windows service. This can be done from Visual Studio using the Windows Service template which can be found under Windows project type.

New > Project > Windows Service > name it as ABCService

We have to add the reference of our WCF DLL in this service. To do this, right click on the project in Solution Explorer and select Add Reference. For adding the dll in reference, select browse tab in the window that appears, and navigate to the folder where our WCF service library is located. In that location, the dll can be found inside the bin-> debug folder. When this dll is added, it is imported into the Windows service app.

Similarly we need reference of System.ServiceModel namespace because it is not available by default in Windows Service. To go to the code window of the service class, go to the design view of the service that is created by default and click on the link ‘click here to switch to code view’. This link will take you to the code view. Import the System.ServiceModel namespace here as it is required for hosting our WCF service. The default service class Service1.cs provides two events – OnStart and OnStop. We can write our code as what we want to do when our service starts and stops in these two events respectively.

The code for this class is written as shown below:

C#
namespace ABCService
{
    public partial class Service1 : ServiceBase
    {
        ServiceHost sHost;
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            sHost = new ServiceHost(typeof(WCFJobsLibrary.Jobs));
            sHost.Open();
        }
 
        protected override void OnStop()
        {
            sHost.Close();
        }
    }
}

We are just creating a service host for our WCF library in the OnStart event and opening it. When the Windows service is closed, it should close the service host, which is specified in the OnStop event.

Merely writing this code will not suffice as we need to have the configurations also. We need to do the configurations in order that the service be hosted and run properly as needed. These configurations are to be specified in App.Config file in the Windows Service app. As this file is not available by default, we have to create it by adding it as a new item and selecting the Application Configuration File template.

The created App.Config file has empty configurations i.e. no configurations are specified under <configuration> tag. Instead of tediously writing all these configurations we can just copy them from the App.Config of our service library. Copy everything available under <system.serviceModel> and paste it in App.Config of the windows service.

Now build the service. This will complete our steps of hosting our WCF service as a Windows service. Now only thing remaining is to register it with Windows OS.

Registering the Service with Windows OS

  1. Go to the design view of the Windows service, right click and click on Add Installer option.
  2. When this is clicked, we get two processes – ServiceProcessInstaller and ServiceInstaller.

    Click on ServiceProcessInstaller and set its account property to local system.

    Now, click on ServiceInstaller and provide some display name, say, ABCNITJobsService. Also choose the start type- manual or automatic or Disabled. In our case we can either choose manual or automatic.

  3. Now build the service once again. This will create an executable file called ABCService(in our case) which can be located in the project directory.

    This exe file should be registered with Windows OS to make it run as a service.

  4. Copy the path of this exe file location and open the containing folder from command prompt.

    In that path write the command InstallUtil ABCService.exe which registers the service with Windows OS.

    Once it is registered with Windows, we can see it in the services list.

    (Control Panel > Administrative Tools > Services)

    If it is not automatically started, we can start it manually by clicking the start link.

  5. When the service is started, we can find whether it is listening or not by using the netstat –a command in the command prompt.

As the service is running as a Windows service there is no need for our WCF service library to be running. We can close it and can use the service from Windows environment.

Note: If Vista OS is used, then it should be run in Administrator mode for hosting the service.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)