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).
- 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 onServiceContract
(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(); }
- 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; } }
- 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(); }
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.
