Getting Started with Windows Communication Foundation
By Rob Windsor, Visual Basic MVP, ObjectSharp Consulting
Windows Communication Foundation (WCF) is Microsoft's next-generation platform for building distributed systems. It was released as part of the .NET Framework 3.0 and is designed to consolidate and extend the APIs from previous versions of the Framework (i.e. ASP.NET Web Services, .NET Remoting, Enterprise Services (COM+), and message queuing).
Getting started with WCF isn't difficult, but there are a couple steps along the way that may not be obvious. This article will walk you through the process of creating and consuming two simple services. To achieve this goal, we need to discuss service classes, service hosts, service configuration, and client proxies.
To follow along with the walkthrough, you'll need Visual Studio 2005, the .NET Framework 3.0, and the Visual Studio 2005 extensions for .NET Framework 3.0 (WCF and WPF) installed.
To start, create an empty solution called GettingStartedWithWCF.
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.
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.
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 EndFunctionEndClass
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="https://localhost:13419/WebServiceHost/Service.svc"
binding="basicHttpBinding"
contract="MathProxy.MathService"
name="BasicHttpBinding_MathService" />
<endpoint
address="https://localhost:8081/HelloService"
binding="basicHttpBinding"
contract="HelloProxy.IHello"
name="BasicHttpBinding_IHello" />
</client>
</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.
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.
Adding in the web project name (WebServiceHost) and the svc file name (Service.svc), we come up with a URL of https://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.
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 https://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.
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="https://localhost:13419/WebServiceHost/Service.svc"
binding="basicHttpBinding"
contract="MathProxy.MathService"
name="BasicHttpBinding_MathService" />
<endpoint
address="https://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.
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 EndSubEndClass
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.
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.