Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Responsive WCF service With ICallbackEventHandler Interface

4.60/5 (5 votes)
8 Feb 2013CPOL5 min read 32.5K   762  
Responsive WCF service with ICallbackEventHandler Interface.

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

Image 1

WCF service placing order

Image 2

Order placed successful or not

Image 3

To accomplish this, the program gets divided in two parts:

  1. Design of a callback WCF service.
  2. 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.

XML
<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:

C#
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. 

C#
[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:

C#
public interface IClientCallBack
{
    [OperationContract(IsOneWay = true)]
    void ISOrerPlaceSuccessfully(bool issuccess, float total);
}

OrderItem - is a datacontract class of the WCF service.

C#
[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.

C#
public class DemoService : IDemoService
{
    public void PlaceOrder(OrderItem item)
    {
        IClientCallBack callback = OperationContext.Current.GetCallbackChannel<IClientCallBack>();
        bool success = true;
        //process order 
        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
ASP.NET
<%@ 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
C#
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.

C#
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.

C#
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.

C#
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.

C#
        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.

License

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