Rashim's Blog

Duplex Service in WCF

Posted on: April 6, 2013

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 channel 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

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

The implementation of this Service Contract,

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

Here CallbackBehavior refers 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,

[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,

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,

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,

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

 

[ServiceContract(CallbackContract = typeof(INotificationServiceCallBack))]
public interface INotificationServices
{
    [OperationContract]
    void SendNotification(string message);
}
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class NotificationServiceCallBack : INotificationServiceCallBack
{
    public void OnNotificationSend(string message)
    {
        Console.WriteLine(message);
        Console.ReadLine();
    }
}
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,

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 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,

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 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>
About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 45 other followers

%d bloggers like this: