Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Duplex Service in WCF

4.94/5 (15 votes)
29 Sep 2013CPOL2 min read 78.5K  
Duplex service in WCF

In WCF, a service can call back to its clients. That is to say that, at the time of call back, the service behaves as a client as well as the client becomes the service, and the client must assist hosting the call back object. To support call back, the underlying transport of the binding must support bidirectional. So, for the nature of connectionless, we cannot use all bindings for the call back operation. For instance, BasicHttpBinding or WsHttpBinding does not support callback while WsDualHttpBinding supports it, because this binding establishes two HTTP channels to do that; one for client to service another for service to client. Also, NetTcpBinding and NetNamedPipeBinding supports callback since their underlying transport is bidirectional.

Callback service can be supported by using CallbackContract property in the ServiceContract attribute. Only one callback contract is allowed for a service contract. After defining a callback contract, we require to set up the client for up keeping the callback and also to offer the callback endpoint to the service for each and every call.

In the example given below, the callback contract defines the operations to which the service can call on the client endpoint.

C#
public interface INotificationServiceCallBack
{
    [OperationContract(IsOneWay = true)]
    void OnNotificationSend(string message);
}

Implementation of this Service Contract, 

C#
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class NotificationServiceCallBack : INotificationServiceCallBack
{
    public void OnNotificationSend(string message)
    {
        Console.WriteLine(message);
        Console.ReadLine();
    }
}

Here CallbackBehavior refers to how the callback of this operation will act which is similar to ServiceBehavior.

And the service contract has been defined like the way given below:

C#
[ServiceContract(CallbackContract = typeof(INotificationServiceCallBack))]
public interface INotificationServices
{
    [OperationContract]
    void SendNotification(string message);
}

To make a way around for the service to call, clients must expose an endpoint to the service. This has been done by the following way:

C#
public class Program
{
    public static INotificationServices Proxy
    {
        get
        {
            var ctx = new InstanceContext(new NotificationServiceCallBack());
            return new DuplexChannelFactory<INotificationServices>
            (ctx, "WSDualHttpBinding_INotificationServices").CreateChannel();
        }
    }
    static void Main(string[] args)
    {
        Proxy.SendNotification("Are u ready?");
        Console.ReadLine();
    }
}

Here InstanceContext is the execution scope of inner most service instance. It provides a constructor that takes the service instance to the host.

And now service implementation,

C#
public class NotificationService : INotificationServices
{
    public INotificationServiceCallBack Proxy
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel
            <INotificationServiceCallBack>();
        }
    }
    public void SendNotification(string message)
    {
        Console.WriteLine("\nClient says :" + message);
        Proxy.OnNotificationSend("Yes");
    }
}

In the above example, we see that the service has the proxy of INotificationServiceCallBack and call its operation which the clients will handle.

That’s it. Now the total code portion that I have used here has been given.

Services and Implementations

C#
public interface INotificationServiceCallBack
{
    [OperationContract(IsOneWay = true)]
    void OnNotificationSend(string message);
}
C#
[ServiceContract(CallbackContract = typeof(INotificationServiceCallBack))]
public interface INotificationServices
{
    [OperationContract]
    void SendNotification(string message);
}
C#
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class NotificationServiceCallBack : INotificationServiceCallBack
{
    public void OnNotificationSend(string message)
    {
        Console.WriteLine(message);
        Console.ReadLine();
    }
}
C#
public class NotificationService : INotificationServices
{
    public INotificationServiceCallBack Proxy
    {
        get
        {
            return OperationContext.Current.GetCallbackChannel
            <INotificationServiceCallBack>();
        }
    }

    public void SendNotification(string message)
    {
        Console.WriteLine("\nClient says :" + message);
        Proxy.OnNotificationSend("Yes");
    }
}

Server and its Configuration

C#
class Program
{
    static void Main(string[] args)
    {
        var svcHost = new ServiceHost(typeof(NotificationService));
        svcHost.Open();
        Console.WriteLine("Available Endpoints :\n");
        svcHost.Description.Endpoints.ToList().ForEach
        (endpoint => Console.WriteLine(endpoint.Address.ToString()));
        Console.ReadLine();
        svcHost.Close();
    }
}
XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="Rashim.RND.WCF.CallBack.Services.NotificationService">
        <host>
          <baseAddresses>
            <add baseAddress = "net.Tcp://localhost:8732/"/>
            <add baseAddress="http://localhost:8888"/>
          </baseAddresses>
        </host>
        <endpoint address="CallbackService" 
        binding="netTcpBinding" contract="
        Rashim.RND.WCF.CallBack.Interfaces.INotificationServices"/>
        <endpoint address="CallbackService" 
        binding="wsDualHttpBinding" contract="
        Rashim.RND.WCF.CallBack.Interfaces.INotificationServices"/>
        <endpoint address="mex" binding="
        mexTcpBinding" contract="IMetadataExchange" />
        <endpoint address="http://localhost:8888/mex" 
        binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata />
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Client and its Configuration

C#
public class Program
{
    public static INotificationServices Proxy
    {
        get
        {
            var ctx = new InstanceContext(new NotificationServiceCallBack());
            return new DuplexChannelFactory<INotificationServices>
            (ctx, "WSDualHttpBinding_INotificationServices").CreateChannel();
        }
    }
    static void Main(string[] args)
    {
        Proxy.SendNotification("Are u ready?");
        Console.ReadLine();
    }
}
XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="http://localhost:8888/CallbackService" 
      binding="wsDualHttpBinding" 
      contract="Rashim.RND.WCF.CallBack.Interfaces.INotificationServices" 
      name="WSDualHttpBinding_INotificationServices">
      </endpoint>
      <endpoint address="net.tcp://localhost:8732/CallbackService" 
      binding="netTcpBinding"
 contract="Rashim.RND.WCF.CallBack.Interfaces.INotificationServices" 
 name="NetTcpBinding_INotificationServices">
      </endpoint>
    </client>
  </system.serviceModel>
</configuration> 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)