WCF (3), Bindings And Channel Stack

In this series of articles, we will discuss the major features of WCF:

A: Introduction

 
Bindings and binding elements are the connection between the application programming model, which includes the attributes and behaviors, and the channel model, which includes the factories and listeners, message encoders, and transport and protocol implementations. Typically, binding elements and bindings are implemented to enable channels to be used by the application layer. --- Microsoft
 
Through the paragraph above, Microsoft told us:
  •  Bindigs are Bridege or Connection between Programming Model and Channel Model
  •  Channel Model

    • Factories
    • Listeners
    • Message Encoders
    • Transport
    • Protocol
 We will discuss Bindings, actually, Channel Model in this article in the following order:
  •  Introduction
  •  Build App
    • Build Service
    • Build Host
    • Build Clients
      • Proxy Approach
      • ChannelFactory Approach
    • Run and Test the App
  •  Discussions
    • Proxy vs. ChannelFactory
    • Channel Stack
 We will build an App to see some details of Channel Model for our discussion.
 

B: Build App --- Proxy vs. ChannelFactory

 
We use the current version of Visual Studio 2019 16.9.3 and the latest version of .NET Framework 4.8 to build the app.
 
We will make a VS solution named as GettingStarted, with four projects, one WCF Service Library and three Console app (for details, see here):
  • WCF Service Library, named GettingStarted, hold the Service Contract
  • Console, named GettingStartedHost, hosting the service
  • Console, named GettingStartedClient, as a client to consume the service
  • Console, named ChannelFactory, as another client to implement ChannelFactory
 
1, Build Service
 
This is the Contract of the ABC of WCF Endpoints, we combine the two default classes in the WCF Service Library project into one, named as GettingStartedService.cs, including both the contracts (service contract and operation contract), and the implementations:
  1. using System.ServiceModel; 
  2. using System;
  3.   
  4. // Tutorial: Get started with Windows Communication Foundation applications  
  5. // https://docs.microsoft.com/en-us/dotnet/framework/wcf/getting-started-tutorial  
  6.   
  7. namespace GettingStartedLib  
  8. {  
  9.     [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]  
  10.     public interface ICalculator  
  11.     {  
  12.         [OperationContract]  
  13.         double Add(double n1, double n2);  
  14.     } 

  15.     public class CalculatorService : ICalculator  
  16.     {  
  17.         public double Add(double n1, double n2)  
  18.         {  
  19.             double result = n1 + n2;  
  20.             Console.WriteLine("Received Add({0},{1})", n1, n2);  
  21.             // Code added to write output to the console window.  
  22.             Console.WriteLine("Return: {0}", result);  
  23.             return result;  
  24.         }  
  25.     }  
 App.config will configure the Address, Binding and Contract of the ABC of WCF Endpoints:
  1.   <services>  
  2.     <service name="GettingStartedLib.CalculatorService">  
  3.       <host>  
  4.         <baseAddresses>  
  5.            <add baseAddress = "http://localhost:8000/GettingStarted/CalculatorService" />  
  6.         </baseAddresses>  
  7.       </host>  
  8.       <endpoint address="" binding="wsHttpBinding" contract="GettingStartedLib.ICalculator">  
  9.         <identity>  
  10.           <dns value="localhost"/>  
  11.         </identity>  
  12.       </endpoint>  
  13.       <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>  
  14.     </service>  
  15.   </services> 
where,
  • service name: Contract name = "GettingStartedLib.CalculatorService"
  • baseAddress: address = "http://localhost:8000/GettingStarted/CalculatorService"
  • binding: binding = "wsHttpBinding"
2, Build Host
 
For the Host, we need to get a reference from the service project, and then step by step as explained in the code (in the file program.cs):
  1. using System;  
  2. using System.ServiceModel;  
  3. using System.ServiceModel.Description;  
  4. using GettingStartedLib;  
  5.   
  6. namespace GettingStartedHost  
  7. {  
  8.     class Program  
  9.     {  
  10.         static void Main(string[] args)  
  11.         {  
  12.             // Step 1: Create a URI to serve as the base address.  
  13.             Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");  
  14.   
  15.             // Step 2: Create a ServiceHost instance.  
  16.             ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress);  
  17.   
  18.             try  
  19.             {  
  20.                 // Step 3: Add a service endpoint.  
  21.                 selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "CalculatorService");  
  22.   
  23.                 // Step 4: Enable metadata exchange.  
  24.                 ServiceMetadataBehavior smb = new ServiceMetadataBehavior();  
  25.                 smb.HttpGetEnabled = true;  
  26.                 selfHost.Description.Behaviors.Add(smb);  
  27.   
  28.                 // Step 5: Start the service.  
  29.                 selfHost.Open();  
  30.                 Console.WriteLine("The service is ready.");  
  31.   
  32.                 // Close the ServiceHost to stop the service.  
  33.                 Console.WriteLine("Press <Enter> to terminate the service.");  
  34.                 Console.WriteLine();  
  35.                 Console.ReadLine();  
  36.                 selfHost.Close();  
  37.             }  
  38.             catch (CommunicationException ce)  
  39.             {  
  40.                 Console.WriteLine("An exception occurred: {0}", ce.Message);  
  41.                 selfHost.Abort();  
  42.             }  
  43.         }  
  44.     }  
3, Build Clients
 
There are two approaches for client, we will discuss separately:

1), Proxy Approach

We need to Add a service reference to the calculator service to build the proxy:
  • In the Solution Explorer window, select the References folder under the GettingStartedClient project, and then select Add Service Reference from the shortcut menu.
  • In the Add Service Reference window, select Discover.
    The CalculatorService service starts and Visual Studio displays it in the Services box.
  • Select CalculatorService to expand it and display the service contracts implemented by the service. Leave the default Namespace and choose OK.
Visual Studio adds a new item under the Connected Services folder in the GettingStartedClient project.
 
After adding this Service Reference, and build the app, we will see the App.config file has been updated, and one proxy (generated code) has been created:
 
App.Config
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.     <startup>   
  4.         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />  
  5.     </startup>  
  6.     <system.serviceModel>  
  7.         <bindings>  
  8.             <wsHttpBinding>  
  9.                 <binding name="WSHttpBinding_ICalculator" />  
  10.             </wsHttpBinding>  
  11.         </bindings>  
  12.         <client>  
  13.             <endpoint address="http://localhost:8000/GettingStarted/CalculatorService"  
  14.                 binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICalculator"  
  15.                 contract="ServiceReference.ICalculator" name="WSHttpBinding_ICalculator">  
  16.                 <identity>  
  17.                     <dns value="localhost" />  
  18.                 </identity>  
  19.             </endpoint>  
  20.         </client>  
  21.     </system.serviceModel>  
  22. </configuration> 
where, 
The serviceModel part has been added that includes the AB, and the name of C of the endpoints. Click the Service Reference, we can see the proxy class that includes the contract, and some additional operations for managing the proxy life cycle and the connection to the service.
  1. //------------------------------------------------------------------------------  
  2. // <auto-generated>  
  3. //     This code was generated by a tool.  
  4. //     Runtime Version:4.0.30319.42000  
  5. //  
  6. //     Changes to this file may cause incorrect behavior and will be lost if  
  7. //     the code is regenerated.  
  8. // </auto-generated>  
  9. //------------------------------------------------------------------------------  
  10.   
  11. namespace GettingStartedClient.ServiceReference {  
  12.       
  13.       
  14.     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel""4.0.0.0")]  
  15.     [System.ServiceModel.ServiceContractAttribute(Namespace="http://Microsoft.ServiceModel.Samples", ConfigurationName="ServiceReference.ICalculator")]  
  16.     public interface ICalculator {  
  17.           
  18.         [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")]  
  19.         double Add(double n1, double n2);  
  20.           
  21.         [System.ServiceModel.OperationContractAttribute(Action="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")]  
  22.         System.Threading.Tasks.Task<double> AddAsync(double n1, double n2);  
  23.                
  24.     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel""4.0.0.0")]  
  25.     public interface ICalculatorChannel : GettingStartedClient.ServiceReference.ICalculator, System.ServiceModel.IClientChannel {  
  26.     }  
  27.       
  28.     [System.Diagnostics.DebuggerStepThroughAttribute()]  
  29.     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel""4.0.0.0")]  
  30.     public partial class CalculatorClient : System.ServiceModel.ClientBase<GettingStartedClient.ServiceReference.ICalculator>, GettingStartedClient.ServiceReference.ICalculator {  
  31.           
  32.         public CalculatorClient() {  
  33.         }  
  34.           
  35.         public CalculatorClient(string endpointConfigurationName) :   
  36.                 base(endpointConfigurationName) {  
  37.         }  
  38.           
  39.         public CalculatorClient(string endpointConfigurationName, string remoteAddress) :   
  40.                 base(endpointConfigurationName, remoteAddress) {  
  41.         }  
  42.           
  43.         public CalculatorClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :   
  44.                 base(endpointConfigurationName, remoteAddress) {  
  45.         }  
  46.           
  47.         public CalculatorClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :   
  48.                 base(binding, remoteAddress) {  
  49.         }  
  50.           
  51.         public double Add(double n1, double n2) {  
  52.             return base.Channel.Add(n1, n2);  
  53.         }  
  54.           
  55.         public System.Threading.Tasks.Task<double> AddAsync(double n1, double n2) {  
  56.             return base.Channel.AddAsync(n1, n2);  
  57.         }  

  58.     }  

Note 
A ICalculatorChannel is created by the proxy class.
 
Then, finally, we have the client code in Program.cs, where using GettingStartedClient.ServiceReference, this is the reference for the proxy class; CalculatorClient class is created by Proxy:
  1. using System;  
  2. using GettingStartedClient.ServiceReference;  
  3.   
  4. namespace GettingStartedClient  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             //Step 1: Create an instance of the WCF proxy.  
  11.             CalculatorClient client = new CalculatorClient();  
  12.   
  13.             // Step 2: Call the service operations.  
  14.             // Call the Add service operation.  
  15.             double value1 = 100.00D;  
  16.             double value2 = 15.99D;  
  17.             double result = client.Add(value1, value2);  
  18.             Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);  
  19.  
  20.             // Step 3: Close the client to gracefully close the connection and clean up resources.  
  21.             Console.WriteLine("\nPress <Enter> to terminate the client.");  
  22.             Console.ReadLine();  
  23.             client.Close();  
  24.         }  
  25.     }  

Now, build the app, we can run it, but we leave it to run with ChannelFactory appoach together.
 
2), ChannelFactory Approach
 
This code is simple, we do not need any reference, no change in app.config, we just need the ABC of Endpoints from service, and write the into the code:
  1. using System;  
  2. using System.ServiceModel;  
  3.   
  4. namespace GettingStartedClient  
  5. {  
  6.     [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]  
  7.     public interface ICalculator
  8.     {  
  9.         [OperationContract]  
  10.         double Add(double n1, double n2);   
  11.     }   
  12.   
  13.     class Program  
  14.     {  
  15.         static void Main(string[] args)  
  16.         {  
  17.             //Step 1: Create an instance of the WCF proxy.  
  18.             //CalculatorClient client = new CalculatorClient(); 

  19.             // This code is written by an application developer.  
  20.             // Create a channel factory.  
  21.             WSHttpBinding myBinding = new WSHttpBinding();   
  22.             EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8000/GettingStarted/CalculatorService");   
  23.             ChannelFactory<ICalculator> myChannelFactory = new ChannelFactory<ICalculator>(myBinding, myEndpoint);  
  24.   
  25.             // Create a channel.  
  26.             ICalculator client = myChannelFactory.CreateChannel();  
  27.   
  28.             // Step 2: Call the service operations.  
  29.             // Call the Add service operation.  
  30.             double value1 = 100.00D;  
  31.             double value2 = 15.99D;  
  32.             double result = client.Add(value1, value2);  
  33.             Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);  
  34.   
  35.             // Step 3: Close the client to gracefully close the connection and clean up resources.  
  36.             Console.WriteLine("\nPress <Enter> to terminate the client.");  
  37.             Console.ReadLine();   
  38.         }  
  39.   
  40.     }  

 where the first part, public interface ICalculator, that is the Contract; in the main, ChannelFactory create a Channel that includes all infor of all ABC of the endpoints.
 
4, Run and Test the App
 
We can test the app from either Visual Studio or from Command Prompt.
 
1), From Visual Studio
 
This approach is only working for the client built with proxy,
  • Open the solution with administrator right
  • Select the GettingStarted folder, and then select Set as Startup Project from the shortcut menu.
  • From Startup Projects, select GettingStarted from the drop-down list, then select Run or press F5.
Note 
In the original program, there are four operational methods, Add, SubTract, Multiply, and Divide, to make it simple, in this article, I only write one method, Add.
 
2), From Command Prompt
 
This approach is only working for the client built with proxy: 
  • Open a command prompt as an administrator, and then navigate to your Visual Studio solution directory.
  • To start the service: Enter GettingStartedHost\bin\Debug\GettingStartedHost.exe. The server will be on, something like this
  • To start the Proxy client: Open another command prompt, navigate to your Visual Studio solution directory, then enter GettingStartedClient\bin\Debug\GettingStartedClient.exe.
  • To start the ChannelFactory client: Open another command prompt, navigate to your Visual Studio solution directory, then enter ChannelFactory\bin\Debug\ChannelFactory.exe.
Server will be,
 
Client
 
 

C: Discussions

 
1, Proxy vs. ChannelFactory
 
There are two approaches to implemente the WCF clients.
 
1), WCF Proxy approach
 
A WCF proxy is a CLR class that exposes the service contract. A Service proxy class has the service contract operations and some additional operations for managing the proxy life cycle and the connection to the service.
 
There are two ways to create a WCF proxy as given below,
  • Using Visual Studio by adding service reference to the client application.
  • Using SvcUtil.exe command-line utility.
2), ChannelFactory approach
 
A channel factory creates channels of different types that are used by client to send messages to the service. ChannelFactory class is used with a known interface to create the channel. This approach is commonly used when you have access control to both the server and the client.
 
3), Proxy vs. ChannelFactory
 
 
2, Channel Stack
 
Now we know, even for the proxy approach, developer does not use and see Channel, but the proxy class makes one Channel for WCF operation.
 
In order to understand WCF Bindings in details, it's important to understand the Channel Stack as part of the WCF runtime.
 
WCF binding is composed of binding elements and each binding element correspond to a specific channel in the Channel Stack. The Channel Stack can be categorized into two major areas i.e. Protocol Channels and Transport Channels. Protocol Channels are Transaction Protocol, Reliable Messaging Protocol and Security Protocol while Transport Channels includes Message Encoding and Transport Protocol.
  • Channel Stack
    • Protocol Channels
      • Transaction Protocol
      • Reliable Messaging Protocol
      • Security Protocol
    • Transport Channels
      • Message Encoding
      • TransportProtocol.
 
 
 
Each request coming from the client will go through a Channel Stack from top to bottom and then an encoded byte stream message will travel over the wire. On the other end, messages travel from the bottom to the top and reaches the service as shown in the above diagram.
 
A complete picture of the WCF runtime with Service Instance, Dispatcher and Channel Stack is as follows:
 
 

Summary

 
This article gave a brief discussion of two WCF features:
  •  Discussions
    • Proxy vs. ChannelFactory
    • Channel Stack
References