Creating a multiple-endpoint service

For traditional distributed communication services, we will open the service over a certain transport-specific endpoint such as HTTP endpoint. If we need to expose it via another different transport layer, we will probably have to add additional code to implement the new endpoint. The WCF programming model separates the service implementation and underlying transport layer so that we can conveniently expose a single service implementation via multiple heterogeneous endpoints (with different a transport layer and binding configuration).

How to do it...

  1. First, we make sure our ServiceContract is ready for various endpoint bindings we want to use to expose it. Some bindings may have special requirements on ServiceContract (such as MSMQ-based bindings). The following sample contract is ready for most built-in bindings:
      [ServiceContract]
        public interface ICounterService
        {
            [OperationContract]
            void Increment();
    
            [OperationContract]
            int GetCurrentCount();
        }
  2. For our sample service, we will implement it as a singleton service so as to demonstrate the “multiple endpoints sharing the same service” behavior. The following code is the implementation of the CounterService:
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
        public class CounterService : ICounterService
        {
            object _syncobj = new object();
            int _count = 0;
    
            public void Increment()
            {   
                lock (_syncobj)
                {
                    _count++;
                }
            }
    
            public int GetCurrentCount()
            {
                return _count;
            }
        }
  3. Finally, we need to add the various endpoints and bindings in the service hosting code. In our sample service, we will use three endpoints/bindings to expose the service—BasicHttpBinding, WSHttpBinding, and NetTcpBinding.
    Uri baseHttp = new Uri(“http://localhost:8731/CounterService/”);
    Uri baseTcp = new Uri(“net.Tcp://localhost:9731/CounterService/”);
    
    using (ServiceHost host = new ServiceHost(typeof(CounterService), baseHttp, baseTcp))
       {
           // Add basicHttpBinding endpoint 
           var basicHttp = new BasicHttpBinding(BasicHttpSecurityMode.None);
           host.AddServiceEndpoint(typeof(ICounterService), basicHttp,”basicHttp”);
    
           // Add wsHttpBinding endpoint
           var wsHttp = new WSHttpBinding(SecurityMode.None, false);
           host.AddServiceEndpoint(typeof(ICounterService), wsHttp, “wsHttp”);
    
           // Add netTcpBinding endpoint
           var netTcp = new NetTcpBinding(SecurityMode.None,false);
           host.AddServiceEndpoint(typeof(ICounterService), netTcp, “netTcp”);
    
           host.Open();
           Console.WriteLine(“service started .........”);
           Console.ReadLine();
       }

How it works...

The previous sample CounterService opens three endpoints over HTTP and TCP transport layers. Client applications can use any of the exposed endpoints to consume the service. The sample client uses ChannelFactory to construct the client channel to consume the service:

string basicHttpAddr = “http://localhost:8731/CounterService/basicHttp”;
string wsHttpAddr = “http://localhost:8731/CounterService/wsHttp”;
string netTcpAddr = “net.Tcp://localhost:9731/CounterService/netTcp”;
// For basicHttpBinding
var basicHttp = new BasicHttpBinding(BasicHttpSecurityMode.None);
_basicHttpFactory = new ChannelFactory<ICounterService>(basicHttp, basicHttpAddr);
_basicHttpClient = _basicHttpFactory.CreateChannel();
// For wsHttpBinding
var wsHttp = new WSHttpBinding(SecurityMode.None, false);
_wsHttpFactory = new ChannelFactory<ICounterService>(wsHttp, wsHttpAddr);
_wsHttpClient = _wsHttpFactory.CreateChannel();
// For netTcpBinding
var netTcp = new NetTcpBinding(SecurityMode.None, false);
_netTcpFactory = new ChannelFactory<ICounterService>(netTcp, netTcpAddr);
_netTcpClient = _netTcpFactory.CreateChannel();

By invoking an increment operation through all the three endpoints, we can find that they’re consuming the same service instance, since the returned count value represents the total service operation calls.

How it works...

See also

  • Complete source code for this recipe can be found in the \Chapter 2\recipe4\ folder