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.
public interface INotificationServiceCallBack
{
[OperationContract(IsOneWay = true)]
void OnNotificationSend(string message);
}
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 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:
[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();
}
}
="1.0"="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();
}
}
="1.0"="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>