Introduction
In this article I am going to discuss about the development of a WCF (duplex) callback service which replies back to
a consumer i.e., client of service, and about an ICallbackEventHandler
Interface implementation which provides a response to the user on screen.
Here I have designed a WCF service which places an order for the client and replies back to the client that the order was placed successfully or not. The
ICallbackEventHandler
is interface implemented on page display response to the client. Before I discuss more, here is a screen that shows the output:
Place Order
WCF service placing order
Order placed successful or not
To accomplish this, the program gets divided in two parts:
- Design of a callback WCF service.
- Design of a Web page with the
ICallbackEventHandler
interface to provide responses on screen.
Design of callback WCF service config file
You need to config a WCF service as below to make the service reliable (duplex), i.e., send
a response back to the client once the task is done.
<configuration>
<system.servicemodel>
<bindings>
<wsdualhttpbinding>
<binding bypassproxyonlocal="true" name="sampleBinding" usedefaultwebproxy="true">
</binding></wsdualhttpbinding>
</bindings>
<services>
<service behaviorconfiguration="returnFaults" name="Service.DemoService">
<endpoint binding="wsDualHttpBinding"
bindingconfiguration="sampleBinding" contract="Service.IDemoService">
</endpoint></service>
</services>
<behaviors>
<servicebehaviors>
<behavior name="returnFaults">
<servicedebug includeexceptiondetailinfaults="true">
<servicemetadata httpgetenabled="true">
</servicemetadata></servicedebug></behavior>
</servicebehaviors>
</behaviors>
</system.servicemodel>
<system.web>
<compilation debug="true">
</compilation></system.web>
</configuration>
The thing to note in the config file is the used protocol called wsDualHttpBinding
that allows to create
a reliable session, that means it allows to send responses back
to the client who made the call to the service once the task gets completed. WSDualHttpBinding - A secure
and interoperable binding that is designed for use with duplex service contracts that allows both services and clients to send and receive messages.
WCF Service file
After creating or modifying, you can code the service file as below:
using System;
using System.ServiceModel;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.Serialization;
namespace Service
{
IDemoService
- interface that is implemented in the WCF service whose method gets called by
the application consuming the WCF service.
[ServiceContract(CallbackContract = typeof(IClientCallBack))]
public interface IDemoService
{
[OperationContract(IsOneWay = true)]
void PlaceOrder(OrderItem item);
}
ServiceContractAttribute.CallbackContract
- This attribute allows to set
the callback contract when the contract is a duplex contract, i.e., the callback interface
that gets called by the service to inform the client. So this allows client applications to listen for inbound operation calls that the server-side service application can send to
the client application which is independent from the client activity. The callback contracts
that have one-way operations represent calls from the service that the client can handle.
IClientCallBack
- interface that is implemented on the client side, i.e., by the application which is consuming the
WCF service.
A method of this interface gets called from the WCF service methods which is discussed below:
public interface IClientCallBack
{
[OperationContract(IsOneWay = true)]
void ISOrerPlaceSuccessfully(bool issuccess, float total);
}
OrderItem
- is a datacontract class of the WCF service.
[DataContract]
public class OrderItem
{
float price;
string name;
int qty;
[DataMember]
public float Price
{
get { return price; }
set { price = value;}
}
[DataMember]
public string Name
{
get { return name; }
set { name = value; }
}
[DataMember]
public int Quantity
{
get { return qty; }
set { qty = value; }
}
}
DemoService
- Implements the service contract interface.
public class DemoService : IDemoService
{
public void PlaceOrder(OrderItem item)
{
IClientCallBack callback = OperationContext.Current.GetCallbackChannel<IClientCallBack>();
bool success = true;
float total = item.Price * item.Quantity;
callback.ISOrerPlaceSuccessfully(success, total);
}
}
PlaceOrder
- method calling the callback contract method ISOrerPlaceSuccessfully
.
OperationContext.Current
- gets the execution context for the current thread. The code uses the
Current
property and the
GetCallbackChannel<t>
method to create a channel back to the caller, i.e., to
the client from the service method. A one-way method allows
the service and client to communicate in both directions independently.
Design of Web page with ICallbackEventHandler interface to provide response on screen
.ASPX file
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="WebClient.Default" EnableSessionState="True"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body id="formBody" runat="server" >
<form id="form1" runat="server">
<div style="text-align:center">
<h1>Place Order</h1>
</div>
<div>
<table>
<tr>
<td><asp:Label ID="lblItemName" runat="server" Text="Label">Item Number :</asp:Label></td>
<td><asp:Label ID="lblItemValue" runat="server" Text="Label">Test</asp:Label></td>
</tr>
<tr>
<td><asp:Label ID="Label1" runat="server" Text="Label">Price :</asp:Label></td>
<td><asp:Label ID="Label2" runat="server" Text="Label">500</asp:Label></td>
</tr>
<tr>
<td><asp:Label ID="Label3" runat="server" Text="Label">Qunatity :</asp:Label></td>
<td><asp:TextBox ID="txtQunatity" runat="server" ></asp:TextBox></td>
</tr>
</table>
<asp:Button ID="Button1" runat="server" Text="Place Order" OnClick="Button1_Click" />
<asp:Label ID="lblMsg" runat="server"></asp:Label></div>
</form>
</body>
</html>
.Cs file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using WebClient.DemoService;
using System.ServiceModel;
using System.Threading;
namespace WebClient
{
Implementation of the client callback interface
Here all methods declared in the service as part of the callback contract gets implemented. This method is called by the service method once
the task is done on the service side.
public class CallBack : WebClient.DemoService.IDemoServiceCallback
{
public string Message
{
get;set;
}
public void ISOrerPlaceSuccessfully(bool issuccess, float total)
{
Thread.Sleep(5000);
if (issuccess)
this.Message = "Order with total of : " + total + " placed successfully";
else
this.Message = "Order with total of : " + total + " failed to place";
}
}
ISOrerPlaceSuccessfully
- is the callback method which gets called back from the
placeorder
method of the service. Once response arrives, the Message
property value gets updated which is used to show the order stats on
the user screen. Note - Thread.Sleep
is used in code just for the delay/demo purpose, i.e., to show how it actually works when
a longer process gets called on service. Remove it when using in the project.
ICallbackEventHandler
Interface - Used to indicate that a control can be the target of a callback event on the server.
ICallbackEventHandler
is a wrapper on XMLHTTP
. It allows to call the server side method without any postback and there is no need to write any
JavaScript/jQuery for making AJAX calls.
public partial class Default : System.Web.UI.Page, ICallbackEventHandler
{
static CallBack callback;
DemoServiceClient client;
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
setupClientSideCallback();
callback = new CallBack();
InstanceContext ic = new InstanceContext(callback);
client = new DemoServiceClient(ic);
OrderItem item = new OrderItem();
item.Name = "Test";
item.Price = 12;
item.Quantity = Convert.ToInt32(txtQunatity.Text);
lblMsg.Text = "Placing Order...";
client.PlaceOrder(item);
}
Here in the above code, the callback is a variable of type Callback
which is static because
when the Order Place button gets clicked, the value of the callback remains as it is.
I.e., it does not get created again for later use in the GetCallbackResult
method, which is discussed below.
OnCallback
is a method defined at the client side which is used to call back once the client script
is registered by the server side code and eventArgs
is a variable defined at the client side which holds the parameter value.
protected void setupClientSideCallback()
{
string ScriptRef = this.ClientScript.GetCallbackEventReference(this, "'" + 0 + "'",
"OnCallback", "'" + lblMsg.ClientID + "'");
formBody.Attributes.Add("onload", ScriptRef);
string script = "<script language="javascript" type="text/javascript">" +
" function getResponse() " +
" { " + ScriptRef + " } " +
" function OnCallback(Result,Context) " +
" { " +
" var lbl=document.getElementById(Context); " +
" lbl.innerText=Result ; " +
" setTimeout(getResponse, 1000); " +
" } " +
"
</script> ";
this.ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientCallback", script);
}
JavaScript code - gets register when the Place Order button gets clicked. After that to check
if a response arrived from the WCF service or not, it makes a call at regular intervals by using
the setTimeout
function. So it works like a timer which executes
a set of code at regular intervals. The ICallbackEventHandler
interface has two methods which
are required to be implemented in the page.
RaiseCallbackEvent
- This event is called when the call from the client side (JavaScript), i.e., from
the browser to the server. This is the event to handle the callback handler. Here
eventArgs
is a parameter which is passed from the client side.
GetCallbackResult
- This method returns the result of the callback event to
the client side, i.e., from the server side to the browser.
string ICallbackEventHandler.GetCallbackResult()
{
if (callback!=null && callback.Message != null)
{
return callback.Message;
}
return "Placing Order..." ;
}
string eventArgument;
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
this.eventArgument = eventArgument;
}
}
}
Summary
WCF reliable (duplex) service is useful to inform the consumer of service, i.e., client of service
whether the task has completed or not by calling the consumer, i.e., client from service. This kind of stuff
is useful when we are creating modules where the payment of the order takes place, as I did in my implementation.