(Print this page)

A self-hosted WCF service
Published date: Saturday, October 16, 2010
On: Moer and Éric Moreau's web site

For one of my client, I had a scenario where I thought that WCF would be the perfect mechanism. One computer on the network is hooked to a data provider. Other computers on the same network needed to query some of that data. So I started to look at WCF but couldn’t find any clear directions/instructions on how to build it.

My major constraint (and that’s where all articles I have seen fall short) is that my service needed to run on a Windows XP computer without IIS (otherwise it would have been too easy!). This is what we call in the WCF jargon, a self-hosted service.

So here is my own article about this wonderful mechanism. You won’t find here a complete discussion on all the specifics of WCF. Some authors have written thick books on that subject (you will find 2 references below here)!

Downloadable demo application

The downloadable demo application contains 2 parts: the host which contains the service and the client application who consume the service.

Both the host and the client are provided in both VB and C#. The downloadable has been built using Visual Studio 2008 (but should run just as is in Visual Studio 2010).

What are the parts of a WCF endpoint?

As you will soon discover, we will create and use endpoints to connect a client to a host. An endpoint is defined by 3 things:

 

  • An address (where will the service be available from)
  • A binding (how to communicate with the service – which protocol to use)
  • A service contract (what are the methods available from the service)

 

These 3 things are often referred to the ABC of WCF: Address, Binding, and Contract.

The host application

Just about any kind of application can be used as the host. For the purpose of this article, I have decided to create a Windows forms application because it is much easier to output tracing to see what’s happening. It could also have been a Console application or, what you will mostly find, a Windows service. A Windows service makes a lot of sense because it can be used even if the computer is not logged in.

After you created your new application, the first thing you need to do is to create something useful of this component.

For this demo application, I have created a very powerful class to add, subtract, multiply, and divide values.

This is the complete code for my cServiceProcess class:

Public Class cServiceProcess

    Public Function Add(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
        Return Value1 + Value2
    End Function

    Public Function Subtract(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
        Return Value1 - Value2
    End Function

    Public Function Multiply(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
        Return Value1 * Value2
    End Function

    Public Function Divide(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
        Return Value1 / Value2
    End Function

End Class

Nothing fancy here, just plain old code. There is nothing related to WCF yet. If you downloaded the demo application, you even see a test button to ensure that this class is working properly. It is always a lot easier to debug your code directly from here before being called through WCF mechanism. The figure 1 shows the application that will become our host service running itself.

Figure 1: Testing the host service by itself

We are now ready to implement WCF specific stuff. We need to add a reference to the System.ServiceModel namespace which contains the classes, enumerations and interfaces necessary to build WCF services and clients.

Figure 2: Adding a reference to System.ServiceModel

We now have to create the contract (one part of the endpoint). The contract is another class that acts like a proxy between the real code and the client. When we will add a reference to this host, it is the contract that will be queried to show what’s available. In .Net terms, this contract is defined in an interface exposing the public members you want your service to provide. I always add this interface to the same file (only if the interface is implemented by a single class as it is the case here) where I have created the class so that it is easier to organize my files.

At the top of your class, you can define the interface like this:

<ServiceContract()> Public Interface IServiceProcess

    <OperationContract()> Function Add(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
    <OperationContract()> Function Subtract(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
    <OperationContract()> Function Multiply(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal
    <OperationContract()> Function Divide(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal

End Interface

As you can see, all the public methods of my class are re-defined here in the contract. There are attributes in front of the class and methods. Those attributes are made available because I have added this line at the top of my file:

Imports System.ServiceModel

The last thing we need to do in the class is to bind the interface with the class. To do this we need to revisit the previous class and modify it like this:

Public Class cServiceProcess
    Implements IServiceProcess

    Public Function Add(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal Implements IServiceProcess.Add
        Return Value1 + Value2
    End Function

    Public Function Subtract(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal Implements IServiceProcess.Subtract
        Return Value1 - Value2
    End Function

    Public Function Multiply(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal Implements IServiceProcess.Multiply
        Return Value1 * Value2
    End Function

    Public Function Divide(ByVal Value1 As Decimal, ByVal Value2 As Decimal) As Decimal Implements IServiceProcess.Divide
        Return Value1 / Value2
    End Function

End Class

Nothing has changed in the code itself, only the declarations. First you will notice the line “Implements IServiceProcess” which indicates that we want this class to implements the interface. Also, each function declared into the interface has to be bound here (otherwise the code won’t compile). This is why methods now have the “Implements IServiceProcess.xxx” where the xxx represents the name of the method we want to bind.

Now that we have the contract and the methods, we have to enable (and possibly disable) the communication to/from the host. This will be the purpose of the Start and Stop buttons on my fHost form.

My start button shows this code:

Try
    'Start the service
    shTest = New ServiceHost(GetType(cServiceProcess))
    shTest.Open()

    'Change the state of the buttons
    btnStart.Enabled = False
    btnStop.Enabled = True

    'Report the new state
    Me.Text = String.Format(kApplicationText, "Started")
    Me.AddItem("Service started.")
Catch ex As Exception
    Debug.WriteLine(ex.Message)
    Me.AddItem("Exception occured while trying to start the service.")
    Me.AddItem(ex.Message)
End Try

Of course we need to declare the shTest variable at the top of the class like this:

Friend Shared shTest As ServiceHost = Nothing

In all those line, the real job is done in these 2 lines:

shTest = New ServiceHost(GetType(cServiceProcess))
shTest.Open()

This is where the service gets instantiated and starts to live.

We are still missing a very important thing before being able to run the application and hit the Start button.

WCF relies on many settings that need to be stored into the application configuration file (app.config). To your host application, add an application configuration file. To this configuration file, add this section into the <configuration> tags (WCFHost, cServiceProcess, IServiceProcess are all related to this demo, you need to change them to fit your own namespace, class name, and interface name):

<system.serviceModel>
  <services>
    <service name="WCFHost.cServiceProcess" behaviorConfiguration="MyServiceTypeBehaviors">
      <!-- Service Endpoints -->
      <endpoint address="" binding="basicHttpBinding" contract="WCFHost.IServiceProcess">
       </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      <host>
        <baseAddresses>
          <add baseAddress ="http://localhost:9876/"/>
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="MyServiceTypeBehaviors">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel> 

You also find somewhere in these settings a tag named baseAddress. This tag is the address where the client will contact our service. Don’t be fooled, even if you see localhost in the URI, that doesn’t mean that IIS will run. The port is mandatory but arbitrary. You can pick just any port that is not already used on your computer.

You might tempted (like I was) to hit the F5 button again. Depending on your version of Windows, you might get another error. On my Windows 7 computer, I got this one:


A first chance exception of type 'System.ServiceModel.AddressAccessDeniedException' occurred in System.ServiceModel.dll
HTTP could not register URL http://+:9876/. Your process does not have access rights to 
this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details).

Like most of you, I am a full administrator of my local computer so why does it complain? It complains because the port you are trying to use (9876 in my demo) is not registered. If you follow the URL from the exception (http://go.microsoft.com/fwlink/?LinkId=70353), it tells you exactly how to fix the problem (if you know what to read!). The things that we need are under the “Configuring Namespace Reservations” section:

netsh http add urlacl url=http://+:9876/ user=DOMAIN\User

You need to open a Command Prompt window with administrator privileges to enter the previous command line (taking care of replacing the correct port number and the correct credentials).

You might also have to run this other command:

netsh http add iplisten ipaddress=0.0.0.0:9876

Now that we have the start button working, we can do the stop button which is way easier:

Try
    'Stop the service
    If (shTest IsNot Nothing) _
        AndAlso (shTest.State <> CommunicationState.Closed) _
    Then
        shTest.Close()
    End If

    'Change the state of the buttons
    btnStart.Enabled = True
    btnStop.Enabled = False

    'Report the new state
    Me.Text = String.Format(kApplicationText, "Stopped")
    Me.AddItem("Service stopped.")
Catch ex As Exception
    Debug.WriteLine(ex.Message)
    Me.AddItem("Exception occured while trying to stop the service.")
    Me.AddItem(ex.Message)
End Try

At this point, your service is ready to be used and if it is running and started, it is ready to receive request from clients. This is our next step.

Note: There is an unsupported tool called HttpNamespaceManager that can help you providing access to your ports. You will find this tool at http://blogs.msdn.com/b/paulwh/archive/2007/05/04/addressaccessdeniedexception-http-could-not-register-url-http-8080.aspx.

The client application

Most of the hard stuff has been done in the host service. Consuming the service is way easier.

Because the client consumes a WCF service, it also requires a reference to the System.ServiceModel namespace. So add it like you did for the host project.

You also need to add a reference to your service but it isn’t a regular reference because the DLL is not available from a local resource. It will be a Service Reference. But before being able to add your service reference, your service must run. I usually start it by double-clicking my component from a Windows Explorer dialog. And don’t forget to click the Start button.

Now back to your WCFClient project, right-click the client project and select “Add Service Reference…”. This will open the “Add Service Reference” dialog as shown in figure 3. In this dialog, you need to enter the address of your service (mine I running at http://localhost:9876/). After you typed your URI, press the Go button to discover your service. If your service is found, the list under Service will display what was found at that URI and clicking one of the services (if you have more than one) will display the operations (methods) on the right. Before clicking the OK button, you have the opportunity to change the name of the namespace of the service proxy. By default, it is set to ServiceReference1. This namespace will be used in your application to access the service.

Figure 3: Adding a reference to our service

After you clicked the OK button, a App.Config file is added to the client project if you don’t already have one. All the dynamic settings are stored in that file. You won’t have to play with that file until you will deploy your service to its final destination. At that time, you will need to change the address of the service (which is currently set to localhost).

We are almost done!

To use everything we did so far, simply add this code to a button:

Try
    Dim x As New ServiceReference1.ServiceProcessClient

    Me.lstResults.Items.Add("Calling the service through WCF...")
    Me.lstResults.Items.Add("20 + 4 = " + x.Add(20, 4).ToString)
    Me.lstResults.Items.Add("20 - 4 = " + x.Subtract(20, 4).ToString)
    Me.lstResults.Items.Add("20 * 4 = " + x.Multiply(20, 4).ToString)
    Me.lstResults.Items.Add("20 / 4 = " + x.Divide(20, 4).ToString)
    Me.lstResults.Items.Add("WCF tests completed...")
    'We should always close the service
    x.Close()
Catch ex As Exception
    Me.lstResults.Items.Add("Exception occured while trying to use the service.")
    Me.lstResults.Items.Add(ex.Message)
End Try

This code is mostly the same we had to test the host from within it. The only difference is the declaration of the object. We need to go through the proxy (the service reference) to access the class.

If your host is still running, you can hit the F5 key and click your test button. It should work.

Testing and debugging

Because so many things can go wrong in the configuration of your host service, it is much easier to test and debug locally. Once you have it working on a single computer, you will eventually want deploy it to other computers.

Say now that something is not working as you would like. In other words, there is a bug!

You can debug your host service and your client application really easily using a great feature of Visual Studio. Right-click your solution from the Solution Explorer dialog and select the “Set StartUp Projects…” item from the contextual menu. The solution property page dialog will be displayed as shown in figure 4.

In this dialog, select the “Multiple startup projects” option and set the action to Start for both projects you want to start.

Figure 4: The solution property pages

With these settings, whenever you will hit the F5 key now, both projects will start. You can set breakpoints into any of these 2 projects as if they were a single project and the debugger will stop on that line whenever the line is hit.

Figure 5: Both projects started from Visual Studio

In figure 5, you also see on the left window (the host), a trace of the method called with values. This can be helpful for debugging.

To be able to do this, I needed to add a public method in my form to access the list box like this:

Public Sub AddItem(ByVal pMessage As String)
    lstResults.Items.Add(Date.Now.ToString("yyyy.MM.dd HH:mm:ss") + " - " + pMessage)
End Sub

And now from the service class, whenever you want to display something on that listbox, you can use this line:

DirectCast(Application.OpenForms(0), fHost).AddItem(String.Format("Method {0} was called. Value1 = {1}. Value2 = {2}", "Add", Value1, Value2))

In this case, I can safely use Application.OpenForms(0) because I am sure that I have a single form in my host application.

Deployment of the host service

You can take the bin folder of your host service and copy it to another computer (where the .Net Framework is already installed of course). You can now run your WCFHost.exe application and click the start button. Notice that you will probably have to run the netsh command (see above) again on this new computer.

Once the host is running on another computer, you need to do a small modification to your client app.config file to direct the client to the new computer where the host is running. So open the App.Config file and find this section:

<endpoint address="http://localhost:9876/" binding="basicHttpBinding"
    bindingConfiguration="BasicHttpBinding_IServiceProcess" 
    contract="ServiceReference1.IServiceProcess"
    name="BasicHttpBinding_IServiceProcess" />

The only modification you need to do is to change the “localhost” found in the address for the address of the other computer.

Now, run the client and click the Test button. You might have another problem contacting your host service, your firewall. To do your first test, I suggest that you turn it off the time you do your test.

Related books

If you think WCF is something you can benefit of, you should really spend some dollars on a good book.

I would recommend 2 books on that topic.

The first book is titled “Learning WCF – A Hands-on Guide” by Michele Leroux Bustamante published by O’Reilly Media (ISBN 978-0-596-10162-6).

The second book is titled “Programming WCF Services, Second Edition – Building Service Oriented Applications with Windows Communication Foundation” by Juval Lowy published by O’Reilly Media (ISBN 978-0-596-52130-1).

Conclusion

It is not as simple as many other articles I have written but it really worth it. Running processes on other computers of your network is very helpful when only some computers have access to limited resources.

Of course reading this short article is not helping you mastering all the settings of WCF and of course we haven’t explored all the features of WCF. It is so flexible and powerful that authors have written complete and thick books on that subject.


(Print this page)