To start, create an empty solution called GettingStartedWithWCF.
.png)
Service Classes
The service class implements the functionality exposed by the service operations. This is just a regular .NET class with no restrictions in terms of inheritance. To enable a class to become a service class, we apply the ServiceContract and OperationContract attributes from the System.ServiceModel namespace. These attributes can decorate the class declaration itself or simply the declaration of an interface that is implemented by the class. Using these attributes does not mean that the class is a service. They simply mean that the class may be exposed as a service.
We will add a Class Library project for our service classes. To do this, right-click the solution, choose Add | New Project, and create a Class Library project called ServiceLib.
.png)
Rename the file Class1.vb to MathService.vb. The class name should change to MathService automatically. If it doesn't, then make the change manually.
Add a reference to System.ServiceModel. You will be using it regularly, as this assembly is the heart of WCF.
.png)
Add two methods to the MathService class: one that adds two integers (called Add) and one that squares a double (called Square). Add an Imports statement for the System.ServiceModel namespace and then decorate the class with the ServiceContract attribute and the methods with the OperationContract attribute.
Imports System.ServiceModel
<ServiceContract()> _
PublicClass MathService
<OperationContract()> _
PublicFunctionAdd(ByVal x AsInteger, ByVal y AsInteger) _
AsInteger
Return x + y
EndFunction
<OperationContract()> _
PublicFunction Square(ByVal x AsDouble) AsDouble
Return x * x
EndFunction
EndClass
As mentioned previously, the attributes can be applied to an interface as well. To demonstrate this, add a new class to the ServiceLib project called HelloService and add the code shown below.
Imports System.ServiceModel
<ServiceContract()> _
PublicInterface IHello
<OperationContract()> _
Function SayHello(ByVal name AsString) AsString
EndInterface
PublicClass HelloService
Implements IHello
PublicFunction SayHello(ByVal name AsString) _
AsStringImplements IHello.SayHello
Return"Hello " + name
EndFunction
EndClass
Service Hosts
To make the functionality contained within the service class available to client applications, we need a service host. This is an application that will listen for requests from clients and create instances of the service class to process those requests. With WCF, you can use IIS as the host, or you can create your own custom application.
We will create both kinds of hosts, starting with web hosting. Add a web site by right-clicking the solution, choosing Add | New Web Site, and choosing the WCF Service template. Make sure to set the location to a file system path in a folder under the solution file.
.png)
Take a brief look at the boilerplate code in Service.vb. The template assumes that we are going to code the functionality for our service here, so it has a sample interface and service class with the proper attributes (as well as a DataContract, which is beyond the scope of this article). We have already created our service classes in the ServiceLib project, so we can delete the Service.vb file.
Open Service.svc and take a look at the values of the attributes. Web hosted services use the same model as previous ASP.NET technologies. They link the file to which you navigate with the file that contains the code. This is the same code-behind technique used in ASP.NET. Because we already have a compiled service class, we can remove this attribute and then put the fully qualified name of our MathService type as the value for the Service attribute.
<% @ServiceHost Language=VB Service="ServiceLib.MathService" %>
Finally, add a reference to the ServiceLib project.
.png)
Now build the solution. After the build, the solution explorer should look like this.
.png)
That's good for the web hosted service. We will now move on to using a custom application as a host. For this, you can use a Windows Forms application, a Console application, or a Windows service. We will use a Console application.
Right-click the solution and choose Add | New Project. Create a Console application named ConsoleServiceHost.
.png)
We will need references to System.ServiceModel (for the class that will do the hosting) and ServiceLib (for our service class), so add those references first.
Now, to have a .NET application host a service is very simple. Just create an instance of the ServiceHost type, pass it the Type for the service class it will host, and then call the Open method. The Open causes the host to start listening for requests. Because we are using a Console application, we will add a call to ReadLine to keep the application running after the host has been opened. We will also create the host with a Using statement to ensure it is closed and disposed of properly when the Console application is shut down. We used our MathService previously, so we will use the HelloService in this project.
Imports System.ServiceModel
Module Module1
Sub Main()
Dim t As Type = GetType(ServiceLib.HelloService)
Using host AsNew ServiceHost(t)
host.Open()
Console.WriteLine("Service started...")
Console.ReadLine()
End Using
EndSub
EndModule
Service Configuration
You may have noticed that, when we set up the hosts, we did not indicate how the client should communicate with the service. This is achieved through service configuration. And while the configuration can be specified in code, it will most often be done using XML in a .config file.
For a client to communicate with a service, it needs an endpoint. An endpoint is made up of three components: an address, a binding, and a contract. These are commonly known as the ABCs of WCF. The address is the URI that identifies where the service can be found. The binding is used to define the rules of the communication (e.g., the encoding, whether the messages need to be secured, how the service will authenticate the client, etc.). Finally, the contract defines the operations exposed by the service. This is basically a reference to the service class.
WCF comes with several built-in bindings that are configured for common business uses (see the table below). You can customize these bindings through configuration, or you can create your own custom bindings to suit the needs of your system. To keep things simple, we will use the basic HTTP binding, which follows the SOAP 1.1 protocol.
Name | Transport | Encoding | Interop |
basicHttpBinding | HTTP/HTTPS | Text | Yes |
netTcpBinding | TCP | Binary | No |
netPeerTcpBinding | P2P | Binary | No |
netNamedPipeBinding | IPC | Binary | No |
wsHttpBinding | HTTP/HTTPS | Text, MTOM | Yes |
wsFederationBinding | HTTP/HTTPS | Text, MTOM | Yes |
wsDualHttpBinding | HTTP/HTTPS | Text, MTOM | Yes |
netMsmqBinding | MSMQ | Binary | No |
netIntegrationBinding | MSMQ | Binary | Yes |
We now need to configure our hosts. Let's start with the Console application. First, we need a place to put the configuration information, so we will add an app.config file. Right-click the project and choose Add | New Item, then create an Application Configuration File using the default name (app.config).
To configure the endpoint, we need an address, binding, and contract. We'll take a look at these in reverse order. The contract has already been determined by the type we gave the ServiceHost constructor. For our application, it will be ServiceLib.IHello. The binding will be basicHttpBinding as mentioned earlier. For the address we will use "http://localhost:8081/HelloService", meaning that clients will need to send messages via HTTP to port 8081 on our local server using HelloService as the application name.
To use the proxy creation wizard described in the next section, we need to take one additional step. Unlike ASP.NET Web Services, WCF services do not automatically expose a service description (or metadata). To enable this feature, we need to add a service behavior and tell the service to use the new behavior.
Replace the entire contents of the app.config file with the XML document below to configure the Console host.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexEnabled">
<serviceMetadata
httpGetEnabled="true"
httpGetUrl="http://localhost:8081/HelloService" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="mexEnabled"
name="ServiceLib.HelloService">
<endpoint
address="http://localhost:8081/HelloService"
binding="basicHttpBinding"
contract="ServiceLib.IHello" />
</service>
</services>
</system.serviceModel>
</configuration>
Configuring the web host is much the same. The differences being that the XML configuration goes in the web.config file, that we do not need to indicate an address for our endpoint (it will be determined by the address of the Service.svc file on our web server), and that the contract being exposed by our web host is MathService, not HelloService.
Replace the entire contents of the web.config file with the XML document below to configure the Web host project.
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexEnabled">
<serviceMetadata
httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="mexEnabled"
name="ServiceLib.MathService">
<endpoint
address=""
binding="basicHttpBinding"
contract="ServiceLib.MathService" />
</service>
</services>
</system.serviceModel>
</configuration>
Client Configuration and Proxies
Now we are ready to create our client application. Right-click the solution, choose Add | New Project, and create a Windows Application named ServiceClient.
.png)
To be able to consume our services, we need client-side configuration and proxies. The configuration works much the same as the server side, in that we need an endpoint that is compatible with our service hosts. The proxies are classes in the client project that are used to represent the services, enabling us to call the service operations in a strongly typed manner.
Luckily, the Visual Studio Extensions for .NET 3.0 include a wizard that will create the required configuration and proxies for us using the service metadata. All you need to know is the proper URL for the service host.
Let's begin with our web hosted service. Since we chose a file-based web site, we need to know the port number Visual Studio is using. This can be found using the Properties Window. Click the web site project and look for the Port number. In my sample application, the port number is 13419.
.png)
Adding in the web project name (WebServiceHost) and the svc file name (Service.svc), we come up with a URL of http://localhost:13419/WebServiceHost/Service.svc.
To configure the client to use this service and to create the proxy class, right-click the ServiceClient project and choose Add Service Reference. Enter the URL for the service for the Service URI and MathProxy for the Service reference name, and then click OK.
.png)
You should see that our client application now has an app.config file, as well as a MathProxy.map under a folder called Service References. I will discuss these further after we have added a reference to our Console hosted service.
For the Console hosted service, the URL required to access the service metadata is stored in its app.config file. If you look at the httpGetUrl attribute of the serviceMetadata element, you will see that it has been configured to http://localhost:8081/HelloService.
Before we can update the client using the Add Service Reference wizard, we need to start the host. Right-click the ConsoleServiceHost project and choose Debug | Start new instance.
Once the host is running, right-click the ServiceClient project, choose Add Service Reference, and enter the proper URl into the Service URI field and HelloProxy for the Service reference name.
.png)
Once the reference has been added, make sure you stop the Console host. You should see that HelloProxy.map has been added to the Service References folder in our client project.
If you open the app.config file in the ServiceClient project, you will see the XML that has been generated for us by the wizard. It looks complex, but actually it's not. To make it easier to tweak the settings, the wizard puts several attributes in the file with their default settings. Since attributes with default settings do not need to be included in the configuration, the XML document can be simplified to what's shown below.
The configuration contains an endpoint element for each of our hosts and the address and binding attributes of these elements match the values in the hosts. What is different is the contract. This is because the client does not reference our original service class, but instead references a client-side proxy that was generated by the wizard from the service metadata.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:13419/WebServiceHost/Service.svc"
binding="basicHttpBinding"
contract="MathProxy.MathService"
name="BasicHttpBinding_MathService" />
<endpoint
address="http://localhost:8081/HelloService"
binding="basicHttpBinding"
contract="HelloProxy.IHello"
name="BasicHttpBinding_IHello" />
</client>
</system.serviceModel>
</configuration>
The proxy classes are hidden from you in the Solution Explorer. This is because they are generated code that you are not meant to edit. To see them, click the ServiceClient solution and then click the Show All Files button in the Solution Explorer toolbar. Once this is done, you can expand HelloProxy.map and MathProxy.map to see the files.
Now that we have our service references in place, we can build a user interface to test the services. Add three TextBoxes and two Buttons to Form1 using the names shown below the controls in red. Give the TextBoxes default values to make the testing of our service operations easier.
.png)
When we click the SayNameButton, we want to call the SayHello method from our HelloService, passing in the contents of the NameTextBox as a parameter. We can use a MsgBox to display the results. When we click the AddButton, we want to call the Add method of our MathService, passing in the contents of the XTextBox and YTextBox converted to Integers as parameters. Again, we can use a MsgBox to show the result.
PublicClass Form1
PrivateSub SayNameButton_Click( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles SayNameButton.Click
Using ws AsNew HelloProxy.HelloClient
MsgBox(ws.SayHello(NameTextBox.Text))
End Using
EndSub
PrivateSub AddButton_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles AddButton.Click
Dim x AsInteger = Integer.Parse(XTextBox.Text)
Dim y AsInteger = Integer.Parse(YTextBox.Text)
Using ws AsNew MathProxy.MathServiceClient
MsgBox(ws.Add(x, y).ToString())
End Using
EndSub
EndClass
When we run the client, we want to make sure that Console host runs as well (it has to be running to listen for requests to the HelloService). To achieve this, we will use multiple startup projects. Right-click the solution and choose Properties. Configure the startup properties as shown below and click OK.
.png)
Finally, we are ready to run our client and test our services. Press F5 to start the client and the Console host and click the buttons. If you've done everything correctly, you should see the following results (assuming you used the values shown in the screenshot above).
Summary
This article walked you through the process of creating and consuming simple services with WCF, stopping occasionally to discuss the foundations of the technology. In addition to being able to build services, you should now understand service classes, service hosts, service configuration, and client proxies.
Future articles will expand on this material to cover bindings, security, data serialization, instancing, instrumentation, and much more. (Editor's note: Read Using Custom Busines Objects with WCF)
About Rob Windsor
Rob Windsor is a senior consultant and trainer with ObjectSharp Consulting in Toronto, Canada. Rob focuses on the architecture, design, and development of custom business applications using leading-edge Microsoft technologies. Rob is President of the Toronto Visual Basic User Group (TVBUG) and, as a member of the MSDN Canada Speakers Bureau, is a regular speaker at User Group meetings in the Toronto area and across Canada. Rob has been recognized as a Microsoft Most Valuable Professional (MVP) for his involvement in the developer community.