Table of contents

Last updated .

Disclaimer: This is work in progress!

WCF - the basics

Windows Communication Foundation is the name for the Windows platform used for making service-oriented applications for the enterprise and internet. In the context of WCF a service is a component that exposes an interface and has these other c : It is not tied to any particular operating system not tied to any particular implementation platform Is self-contained in terms of its public types. It does not require the client to depend on anything other than the service contract. The service contract (the interface and its associated types) is exposed as WSDL documents and associated XSD documents. Non-functional requirements of the service, such as security or protocol requirements are described independently of the contract.

Clients can call services that run locally, on a different machine or in a different domain. Clients can remain agnostic of where the service is running, as long it is reachable. WCF services are commonly used in an intranet scenario to implement distributed applications. But it may as well be extended to serve clients across domain boundaries. For example, a WCF game service might serve some internet game app that runs on multiple computers, phones or other types of gadgets.

The WCF architecture promotes loose coupling, not only between between services and clients, but also for choice of communication protocol, message encoding formats and hosting environment. Out of the box, WCF supports the protocols HTTP, TCP, Named pipes and MSMQ.

The first, basic WCF sample

I am going to demo a very basic "Hello World"-ish WCF sample. It will involve the service itself and a client that uses it. Specifically, the sample will be comprised of these four components:

First, we need a container for these components - a Visual Studio solution. So, in Visual Studio, create a new blank solution:

New Visual Studio solution
New Visual Studio solution

Defining the service contract

To create the project for the service contract, select the menu item in Visual Studio for creating a new project and select the WCF category. In the list of templates, select the WCF Service Library. I named the new project FirstWcfServiceContract. Clicking OK makes Visual Studio add a Class Library type of project to the solution, with a reference set to the core WCF assembly named System.ServiceModel.

New Visual Studio solution

The project also contains a couple of code files. Delete the app.config, Service1.cs and IService1.cs files and add a new IFirstWcfService.cs file instead. This file is going to contain the interface for the new service. I could go ahead and make the usual "Hello World" sample, but I plan to make things marginally more interesting by making a service that can capitalize a string value. I named the service contract IFirstWcfService and I picked the name Capitalize for the method. This is the code to implement that:

IFirstWcfService.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
using System.ServiceModel; namespace FirstWcfServiceContract { [ServiceContract(Namespace = "http://firstwcfservice.org")] public interface IFirstWcfService { [OperationContract] string Capitalize(string value); } }

The System.ServiceModel.ServiceContract attribute makes this interface a WCF service. And any members of the interface that are marked with System.ServiceModel.OperationContract are part of the public interface of the service. Apart from these attributes, this is just the code for a standard .Net interface. Now you can compile the project.

Implementing the contract

Next, we need a separate class library project to implement the service contract. I could use the same project template as before, but it should be clear by now that we got little benefit from that - all we got was a reference to System.ServiceModel and three files that we didn't need and therefore deleted. This time I will just add a standard class library type of project. So, right-click on the solution in solution explorer and go Add -> New Project. Select the Class Library template and name the project FirstWcfServiceImpl:

Select the class library template for the service assembly

In the new project, set a project reference to the FirstWcfServiceContract project that we created previously. Notice that this project does not need a reference to System.ServiceModel. Now, the code to capitalize a string can be implemented. There is already a Class1.cs code file in the project that we can use. Rename it to FirstWcfService. In that code file add a using statement for the FirstWcfServiceContract namespace and rename the class to FirstWcfService. Add IFirstWcfService to the implementation list and implement the Capitalize method. The contents of the file should be:

FirstWcfService.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
using System.Text; using FirstWcfServiceContract; namespace FirstWcfServiceImpl { public class FirstWcfService : IFirstWcfService { public string Capitalize(string value) { char prev = ' '; StringBuilder sb = new StringBuilder(); foreach (char c in value) { if (char.IsLetter(c) && !char.IsLetter(prev)) sb.Append(char.ToUpper(c)); else sb.Append(c); prev = c; } return sb.ToString(); } } }

The lesson to take away from this class implementation is not the code itself, but the fact that there is nothing "WCF" about this; it is just a normal .Net class that implements an interface.

A host for the service

Third item on the project list is a host for the service. In order for the service to be callable, there must be a running process that can expose the service to the world. There are various options for hosting a WCF service. We are going to keep things as simple as possible for now, and make a standard console app to host the service we just implemented. So, right-click on the solution in solution explorer and go Add -> New Project. Select the Console Application template and name the project FirstWcfServiceHost:

Select the Console application template for the host project
Select the Console application template for the host project

In the new project, set a project reference to the FirstWcfServiceContract and FirstWcfServiceImpl projects. You will also have to add a reference to System.ServiceModel. Also, delete the app.config file; we won't need it in this sample. Add the following code to the Main entry point in the Program.cs file:

Program.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
using System; using System.ServiceModel; using FirstWcfServiceContract; using FirstWcfServiceImpl; namespace FirstWcfServiceHost { class Program { static void Main(string[] args) { const string baseAddress = "http://localhost:9001/FirstWcf"; using (ServiceHost host = new ServiceHost(typeof(FirstWcfService), new Uri(baseAddress))) { host.AddServiceEndpoint(typeof(IFirstWcfService), new BasicHttpBinding(), "FirstWcfService"); host.Open(); Console.WriteLine("Press Enter to terminate."); Console.ReadLine(); } } } }

The above code creates a System.ServiceModel.ServiceHost object to host the service we implemented. The object is fed two pieces of data to get started, namely

With a live host, an endpoint is added. At least one endpoint is needed before a client can call the service. The endpoint is configured through a call to ServiceHost.AddServiceEndpoint with these three properties:

When the host program is run, the service host is first initialized and immediately after that, the main thread is blocked in call to Console.ReadLine, awaiting input from the user. Service calls will still be processed, though, because these are served by additional threadpooll.æ- threads.

To test the host application, right-click the FirstWcfServiceHost project in solution explorer and select Set as Startup Project from the context menu. Then press F5 to compile and run the project. Unfortunately, using Windows 10, I get an exception when I run the project:

System.ServiceModel.AddressAccessDeniedException "The process has no rights to this namespace."

The solution is to close Visual Studio and restart as an administrator. Now, the application can be started with no problems:

The running console host
The running console host

We now have a running service. The next step is to create a client app to consume the service.

Creating a client app

Add a fourth project to the solution. Select the same Console application project template as before and name the project FirstWcfServiceClient. When the project has been created set a project reference to the FirstWcfServiceContract project and also a reference to System.ServiceModel. We need a reference to the FirstWcfServiceContract project in order to be able to write code against the interface it contains.

Change the code in Program.cs to this:

Program.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
using System; using System.ServiceModel; using FirstWcfServiceContract; namespace FirstWcfServiceClient { class Program { static void Main(string[] args) { const string serviceAddress = "http://localhost:9001/FirstWcf/FirstWcfService"; EndpointAddress endpoint = new EndpointAddress(serviceAddress); IFirstWcfService proxy = ChannelFactory<IFirstWcfService>.CreateChannel(new BasicHttpBinding(), endpoint); string output = proxy.Capitalize("test input.test input\r\ntest input."); Console.WriteLine("Service returned '{0}'.", output); Console.WriteLine("Press Enter to terminate client."); Console.ReadLine(); } } }

The above code uses the static System.ServiceModel.ChannelFactory.CreateChannel method to create a channel that acts as a proxy for the service. The channel implements the interface that is referenced in the type parameter, in this case our IFirstWcfService interface. The channel is configured with the same type of binding that the service host uses and the same address too. Now we are ready to test the service and client. First, start the service by pressing Ctrl+F5, then start the client by right-clicking the FirstWcfServiceClient project and select Debug - Start new instance from the context menu:

Starting the client exe
Starting the client exe

As shown in the client console, the service Capitalize method has been called, returning a string with some uppercase letters:

Service and client
Service and client

A reviews of the first service

The above sample solution showcased building a very simple service, together with a host and a client application. All this was partitioned into four assemblies. It's pretty obvious that the client should be separate from the service. But the sample also has the service as a separate assembly from the host. This is the preferred setup that constitutes the least coupling between host and service and also has the practical implication that the service can be moved to a different type of host with no implications for clients. The sample also defined the service contract (the set of interfaces and their supporting types) in an assembly of its own. Again, this is to decouple the service contract from its implementation. Remember that the client is dependent on the contract, not the implementation, so there is no need to distribute the service assembly to clients. (Actually there is no need to distribute the contact either, I'll come to that). Also, having the two in separate assemblies promotes the idea that the same contract could well be implemented by multiple services. Each of those could be using different endpoints, bindings and policies.

In fact, I would argue that the business functionality should also be distilled into its own assembly or set of assemblies. In the above example, the "business" functionality was extremely simple, only involving the processing of a string value, but still, the code for this should have been moved into its own assembly. This way, the business functionality can be reused by clients that may not need to go through a service - clients internal to the application for example. In addition, such a division between service and business component makes the two testable as separate entities. It also makes it possible to version the service independently of the business component. testable as separate entities

Fundamental WCF concepts

I hope that the simple sample developed in this chapter has conveyed that a number of concepts are involved in a WCF solution. WCF clients and services use the types exposed by the System.ServiceModel. I will use this opportunity to introduce the basic concepts of the service model. I will come back to these features and treat them in more detail in later chapters.

Serialization and messages

The client code that calls the service method looks like any other .Net method call. But, in fact, it's quite different. Under the hood, the servicemodel has to serialize the method call, including parameter values and then pass this packet of data over the wire. At the receiving end, the packet must be deserialized and converted into a stack frame that invokes a method. In a similar manner, the method return value is serialized at the service end and then deserialized at the client. In order for this to work, the client and the service have to agree in detail on how messages are formatted. To maximize interoperability between disparate systems, the message is usually serialized to XML that adheres to a common XSD schema.

Services

In WCF, a service is a type adorned with the ServiceContract attribute. The attribute can be applied to a class, thus making the class a service. But usually, it is applied to an interface. A class then becomes a service when it implements the interface.

Service hosts

In this chapter's example, we developed our own host application, which is an executable that uses the System.ServiceModel.ServiceHost type to host a service. But there are other options. The available options are:

No matter which hosting option is used, hosting a service ultimately comes down to instantiating a System.ServiceModel.ServiceHost object. The ServiceHost initializes the channel responsible for processing messages.

Endpoints

An endpoint is what makes a service reachable, in that it's configured with:

A host exposes one or more endpoints. The same contract can be make available through multiple bindings and / or adresses.

Addresses

A service address is a URI, which has the format scheme://domain[:port][/path], for example http://mydomain:8000/api/nameservice. The scheme denotes the protocol used. Valid examples include http, net.tcp, net.pipe and net.msmq.

Bindings

A binding describes the protocols used by the service, specifically:

Endpoint metadata

Clients need information about the service in order to use it. A client can obtain metadata at runtime by invoking a metadata exchange endpoint if the service exposes one. Or metadata can be extracted from a service in the form of a WSDL document, which is XML according to a standard schema. In the example above we used neither of these options. Instead, the client obtained information about the contrat by referencing the assembly containing the service interface, and in addition we simply hardcoded the address and binding according to what the service supported.

Proxies

A proxy is a type that implements the same interface as the service, thus acting as a facade to the real service. The client application uses a proxy to communicate with the service. Behind the curtain, the generated proxy class uses a channel to communicate with the service.

Channels

Communication between client and service goes through a channel. The service host creates a channel listener for an endpoint. At either end, the channel is actually a layered stack of message processors, for example there is a transport layer, a message-encoding layer and other probably further layers related to security, for example.

Behaviors

A behavior further affects how a message is processed. Behaviors are local to a service or a client and thus do not form part of service metadata. Examples of behaviors include features such as authentication or transactions.

Get the code for this chapter's example as a Zip file

Using configuration files to configure a service

In the first sample developed above, there were no configuration files in use. Addresses, bindings and such were hardcoded in the service host. These things are usually factored out of the code and into configuration files. And with good reason: Using configuration files makes the services more maintainable and amenable to testing. So let's change the projects to use configuration. I started out by copying the whole solution and renamed the new solution file FirstWcfWithConfig.

After opening that new solution, we need to add a config file to the FirstWcfServiceHost project. Visual Studio actually comes with a tool to help with that, called Microsoft Service Configuration Editor. Start it by selecting Tools -> WCF Service Configuration Editor from the Visual Studio menu. In the tool, select File -> New Config from its menu. This gives you a UI that looks like this:

Microsoft Service Configuration Editor
Microsoft Service Configuration Editor

Start off by adding configuration for our service: select the Services node in the left-side treeview, then click on the Create a new service link, as shown in the screenshot below. In the ensuing dialog, click on the Browse button. Then, in the next dialog, browse to the FirstWcfServiceImpl.dll assembly and select the only service it exposes: the FirstWcfService service. Click Open, then Next two times.

Select the FirstWcfService service
Select the FirstWcfService service

The next step in the wizard is about selecting a binding for the service. Let's go for TCP:

Select the TCP protocol
Select the TCP protocol

Click Next to go to the next wizard step, where an address is requested. Enter net.tcp://localhost:9001/FirstWcf/FirstWcfService. Click Finish. Save the file by selecting File -> Save from the menu. Name the file app.config and place it in the directory for the FirstWcfServiceHost project. Back in Visual Studio, add the newly generated app.config file to the project. This is what it contains:

app.config
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <services> <service name="FirstWcfServiceImpl.FirstWcfService"> <endpoint address="net.tcp://localhost:9001/FirstWcf/FirstWcfService" binding="netTcpBinding" bindingConfiguration="" contract="FirstWcfServiceContract.IFirstWcfService" /> </service> </services> </system.serviceModel> </configuration>

All configuration in related to WCF goes inside a system.serviceModel element. In the generated file, a service node has been added to represent the service, and, under that, a node to represent the single endpoint we have defined so far. It has attributes corresponding to the properties that we have set in code until now, specifically the address, binding and contract. Instead of giving a full address for the service it's more practical (if you have more than one endpoint) to define a baseaddress once and have each service use a relative address. Also, I would like to give a name to the endpoint node. These two changed are reflected in this modified version of the configuration:

app.config
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <services> <service name="FirstWcfServiceImpl.FirstWcfService"> <endpoint address="FirstWcfService" name="tcpEndpoint" binding="netTcpBinding" bindingConfiguration="" contract="FirstWcfServiceContract.IFirstWcfService" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:9001/FirstWcf"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>

Now, the base address has been factored out into a host node as a child of the service node. I would also like support for metadata exchange to the service. This will enable a client to obtain metadata information from the service at runtime. That information will contain all a client needs to connect to and call the service. I added support for this by manually editing the config file to this:

app.config
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="mexBehavior"> <serviceMetadata /> </behavior> </serviceBehaviors> </behaviors> <services> <service name="FirstWcfServiceImpl.FirstWcfService" behaviorConfiguration="mexBehavior"> <endpoint address="FirstWcfService" name="tcpEndpoint" binding="netTcpBinding" bindingConfiguration="" contract="FirstWcfServiceContract.IFirstWcfService" /> <endpoint binding="mexTcpBinding" name="mex" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:9001/FirstWcf"/> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>

Now, there is a new behavior node with a serviceMetadata child node. This bit of configuration enables metadata exchange for any service. Our FirstWcfService service then makes use of it by referencing in its behaviorConfiguration attribute the name I give it: mexBehavior. In addition, an endpoint is defined for the IMetadataExchange contract.

We can now modify the code for the host project. Details about addresses and bindings now need to go, because that is all defined in configuration now. So, the somewhat leaner code for the host now looks like this:

Program.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
using System; using System.ServiceModel; using FirstWcfServiceImpl; namespace FirstWcfServiceHost { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(FirstWcfService))) { host.Open(); Console.WriteLine("Press Enter to terminate."); Console.ReadLine(); } } } }

Comparing this code with its previous version (see above), it is evident that there is no more mention of addresses and no endpoint is created. The service model now creates the endpoint for us, thanks to the configuration. The only value we provide in instantiating the host is the type of FirstWcfService. At runtime, the service model matches the name of this type to the available entries in the services section in the configuration. In this case, a match is found, because we gave the value of FirstWcfServiceImpl.FirstWcfService as the value of the name attribute of the service node.

Modifying the client to use configuration

We can now start modifying the client project as well to get metadata info from the service instead of hardcoding values for address and binding type and instead of referencing the assembly with the service contract directly. In the FirstWcfServiceClient project start by deleting the app.config file. Also delete the reference to the FirstWcfServiceContract assembly. We won't need it anymore, because we are going to import the contract type through metadata exchange in a little while.

WCF comes with a command-line tool called svcutil that can be used for a variety of WCF-related tasks. It can call the metadata exchange of a running service and from that generate C# code for a client proxy and generate an app.config for a client too. Let's use that to generate some code for us. As mentioned, it requires a running service. So, ensuring that you are running Visual Studio as an administrator and that the FirstWcfServiceHost project is set as the startup project, you can now start the service by pressing Ctrl+F5 in Visual Studio. You should see the console appearing with the message "Press Enter to terminate".

Then, open a Visual Studio command prompt and type the following command to invoke svcutil:

svcutil /d:{path to project folder} /o:FirstWcfServiceProxy.cs /namespace:*,FirstWcfService /config:app.config net.tcp://localhost:9001/FirstWcf

For the /d switch you need to enter the path to the FirstWcfServiceClient project folder. The parameters fed to svcutil are:

When this command is run, svcutil generates a configuration file with the necessary settings for invoking the service and a C# code file containing a few interfaces and classes that have been synthesized from the metadata obtained from the service:

Running svcutil to generate configuration and proxy code files
Running svcutil to generate configuration and proxy code files

The interface that the service implements has been regenerated as such:

FirstWcfServiceProxy.cs (partial)
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
[System.ServiceModel.ServiceContractAttribute(Namespace="http://firstwcfservice.org", ConfigurationName="FirstWcfService.IFirstWcfService")] public interface IFirstWcfService { [System.ServiceModel.OperationContractAttribute(Action="http://firstwcfservice.org/IFirstWcfService/Capitalize", ReplyAction="http://firstwcfservice.org/IFirstWcfService/CapitalizeResponse")] string Capitalize(string value); [System.ServiceModel.OperationContractAttribute(Action="http://firstwcfservice.org/IFirstWcfService/Capitalize", ReplyAction="http://firstwcfservice.org/IFirstWcfService/CapitalizeResponse")] System.Threading.Tasks.Task<string> CapitalizeAsync(string value); }

As you can see, in addition to the Capitalize method that we added to the interface, a new CapitalizeAsync method has been created. The FirstWcfServiceClient class represents the service class. It contains five constructors that I left out for brevity:

FirstWcfServiceProxy.cs (partial)
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
public partial class FirstWcfServiceClient : System.ServiceModel.ClientBase<FirstWcfService.IFirstWcfService>, FirstWcfService.IFirstWcfService { // Constructors left out for brevity public string Capitalize(string value) { return base.Channel.Capitalize(value); } public System.Threading.Tasks.Task<string> CapitalizeAsync(string value) { return base.Channel.CapitalizeAsync(value); } }

The FirstWcfServiceClient class inherits from System.ServiceModel.ClientBase and uses its Channel property to invoke service methods. Now, the code in the Main entry point of the client exe can be modified to use the generated class and interface:

Program.cs
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
using System; namespace FirstWcfServiceClient { class Program { static void Main(string[] args) { FirstWcfService.IFirstWcfService proxy = new FirstWcfService.FirstWcfServiceClient(); string output = proxy.Capitalize("test input.test input\r\ntest input."); Console.WriteLine("Service returned '{0}'.", output); Console.WriteLine("Press Enter to terminate client."); Console.ReadLine(); } } }

Gone are the explicit calls to create and endpoint and a channel. The client exe can now be started. It gives the same output in the console as in the previous sample:

Service and client
Service and client

The call svcutil generated the following app.config file in the client project. It contains the configuration for a client endpoint using tcp binding and the address of the service. Notice that the configuration for a WCF client goes in a client node:

app.config
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding name="tcpEndpoint" /> </netTcpBinding> </bindings> <client> <endpoint address="net.tcp://localhost:9001/FirstWcf/FirstWcfService" binding="netTcpBinding" bindingConfiguration="tcpEndpoint" contract="FirstWcfService.IFirstWcfService" name="tcpEndpoint"> </endpoint> </client> </system.serviceModel> </configuration>

Next : Controllers >


2016 by Niels Hede Pedersen Linked in