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

COMET: Multi-client continuous PUSH-update grid for ASP.NET web apps - Part 2

4.92/5 (41 votes)
29 Nov 2009CPOL10 min read 169.8K   4.4K  
Scalable, high performance, and low latency reverse AJAX grid. This is a COMET based multi-client capable grid control. This grid control can COMET-update to multiple clients with varying data needs. Complete performance report included.

Note: Your feedback is important to me. If you find this article useful, please leave a comment at the bottom and a vote.

Introduction

Nature is so amazing that almost no two entities are identical; every entity is unique. No two human faces are identical; every one is unique. Likewise, business needs are never the same, but are unique. This brings a need to design customized software systems for every business even if they are in the same industry - say finance or healthcare. I.e., no two finance companies competing in the same area are satisfied with the same set of software. Their methodology differ, requirements differ, design differ. This makes one of them more successful than the other.

After an article on the COMET grid control (click link), this article focuses on taking a step further in the area with a specialized case. In the previous article, we discussed on a generalized COMET grid control. In this article, we will discuss on using the COMET approach to update multiple clients with varying data. That is, consider we have an ASP.NET page that displays weather information. A web client, say Alice, sits in NYC, and another web client, say Bob, sits in Chicago. Obviously, we need to post different weather data to Alice and Bob. The previous article's COMET grid control only has the capability to update all the clients with the same data. That will not serve well for our requirement here. We need to update Alice and Bob with different data. This article explains a grid control with that capability: a COMET a multi-client update grid control. As always, fully working samples are available for download.

A more professional example: consider a finance company having many trading desks. Each trading desk concentrates on performing trade in a specific area. Bob is a trader responsible for a portfolio named 'Portfolio 1'. Alice happens to be managing 15 such portfolios, say Portfolio 1 to 15. Consider they require an ASP.NET web application to track their P/L (Profit/Loss) every moment, as and when it happens. As stated above, the COMET grid presented in the previous article will not serve our needs here. This is because Alice needs the total P/L (profit/loss) of 15 portfolio(s), whereas Bob just cares about 'Portfolio 1' for which he is responsible. Hence, we need a COMET grid that can post different data to different clients. Using this multi-client COMET grid, this business case can be production-ready in few hours. A sample for this case is also available for download.

Samples: COMET multi-client grid in ASP.NET web apps

Sample 1: The below is a sample P/L trade application's display page. Note that Alice is getting the *total* (Portfolio 1 to 15) P/L update while Bob gets just Portfolio_1's P/L update. Also, note the address bar of Bob and Alice's browsers. They both refer to the same address (thus the same grid control), but the data posted (using COMET) is different.

Bob's browser window:

BobView.gif

Alice's browser window:

AliceView.gif

Sample 2: Shown below is a sample weather application's display page. Note that Alice is getting a different update than Bob. But the code uses only one COMET grid control to update both Alice and Bob.

Alice's browser window:

AliceWeather.gif

Bob's browser window:

BobWeather.gif

How would you like to read?

It is best to read the article sequentially. However, not everyone is in the same situation. So here is a brief guideline to get what you are looking for quickly:

  • If you are looking at quickly grabbing the control to use in your project, then you just need to read the section 'Using the code'.
  • If you are curious about the design, read the section 'Explanation of the COMET multi-client grid design'.
  • If you would like to read about the data structure used in this project, read about the MultiMap generic collection here.
  • The References section suggests further reading in this subject.

Using the code

Prerequisite: Visual studio 2008.

Quick demo: Download the sample project from the links at the top. Run it in VS2008. Copy the link in the address bar. Open another browser instance. Paste the link in the address bar. Now you can see Alice's weather information in the Browser_1 instance and Bob's in the Browser_2 instance.

Using this COMET based grid control in three easy steps:

  1. Add this control to your project:
    1. Right click Toolbox | Select 'Choose items...'| Click Browse. Then, browse to select the downloaded (from the first line in this article) assembly (GridControlCometAjax.dll).
    2. From the Toolbox, drag and drop the newly added control to your web page.
  2. Add an asynch handler in your web.config file to the section 'httpHandlers' as below:
  3. XML
    <add verb="GET, POST" path="GridControlCometAjax.ashx" 
      type="BK.Util.GridAsynchHandler, GridControlCometAjax" validate="false">
  4. In your code-behind (say, Default.aspx.cs), add the below line in your 'Page_Load' method. Remember, you need to call the method LoadControl before making any client update calls using Dyn methods (which are explained below).
  5. C#
    protected void Page_Load(object sender, EventArgs e)
    {
       // Your code goes here.
       // Assuming the control instance's name
       // is GridControlCometAjax1, add the below line:
       GridMultiClientControlComet1.LoadControl(this);
    }

Well, that's it. Now start using the control. Whatever you update in this grid control using any method prefixed 'Dyn' will be automatically propagated to the client. Additionally, these methods are thread-safe. So you can call these methods from multiple threads.

Use-case 1: How to declare and add elements to (or initialize) this multi-client COMET grid control.

C#
using System;
using BK.Util;
    
namespace WeatherWatch 
{
   public partial class _Default 
   {
      // Other declarations can go here
      protected global::BK.Util.GridMultiClientControlComet 
                GridMultiClientControlComet1;   
      // Other declarations go here.
   }
}

File default.aspx.cs:

C#
using System;
using BK.Util;
    
namespace WeatherWatch
{
    public partial class _Default : System.Web.UI.Page
    {  
        protected void Page_Load(object sender, EventArgs e)
        {
            GridMultiClientControlComet1.LoadControl(this);
    
            // Error checks are avoided for brevity
            int rowCount, colCount;
            // Get the Number of rows available to update
            GridMultiClientControlComet1.DynGetRowCount(
                     this.Session.SessionID, ref rowCount);
            // Get the number of Columns available to update
            GridMultiClientControlComet1.DynGetColCount(
                     this.Session.SessionID, ref colCount);

            // Modify values of each Row element to value RowItem*ColItem;
            for (int RowItem = 0; RowItem < rowCount; RowItem++)
            {
                for (int ColItem = 0; ColItem < colCount; ColItem++)
                {
                    int Update = RowItem * ColItem;
                    GridMultiClientControlComet1.DynModifyTableItem(this.Session.SessionID, 
                                   RowItem, ColItem, Update.ToString(), false);
                }
            }
           
        }
   }
}

In the above code, the first line loads the control [as stated in step (3)]. The next lines loops through to fill in the elements.

Use-case 2: How to update the background color of this COMET grid control.

Code explanation is available inline:

C#
// Parameter 1 is Session Id
// Parameter 2 is Row 
// Parameter 3 is Column
// Parameter 4 is Color to update
// Parameter 5 states if the update need to be propogated to the client or not.
GridMultiClientControlComet1.DynModifyTableFColor(SessionID, 0, 0, 
                      System.Drawing.Color.LightBlue, false);

Use-case 3: How to update the foreground or text color of this COMET grid control.

Code explanation is available inline:

C#
// Parameter 1 is Session Id
// Parameter 2 is Row 
// Parameter 3 is Column
// Parameter 4 is Color to update
// Parameter 5 states if the update need to be propogated to the client or not.
GridMultiClientControlComet1.DynModifyTableFColor(session1, 0, 0, 
                             System.Drawing.Color.DarkBlue, false);

Use-case 4: How to modify the background image dynamically.

Code explanation is available inline:

C#
// Parameter 1 is Session Id
// Parameter 2 is Row 
// Parameter 3 is Column
// Parameter 4 is ralative Image path to update
// Parameter 5 states if the update need to be propogated to the client or not.
    
GridMultiClientControlComet1.DynModifyBackImage(session1, 0, 0, "Img2/Rain.gif", false);

You would have noticed that the only difference in using this COMET grid control compared to the previous article's (regular COMET) control is the SessionID parameter. This parameter tells the grid which client to send the update to. All other clients are then excluded from the update.

Performance results

I performed a stress test using Microsoft's Web Application Stress test tool. A complete report is attached as a downloadable file in the link above. However, an overview is given below. I used 100 threads with 10 sockets per thread, which means 1000 concurrent connections. I used the test application that updates the clients with two grid controls. Here are the results:

Performance.jpg

Explanation of the COMET multi-client grid design

If you are just planning to use this COMET grid control, you may skip this section. However if you are curious to look in, continue reading.

In short, as in the below picture, when the client connects, the call is kept pending. When the data needs to be updated from the server side, the particular client's call that was kept pending is taken up. This call is completed by filling in the data that needs to be updated. The other clients are not updated, and their calls are kept pending.

Image 6

Also, a maintenance update is performed in a pre-determined time interval to all the clients so that all the connected clients are not disconnected due to browser timeouts.

To explain in detail:

  • Step 1: Client makes the call to the server.
  • Step 2: Server returns the call with an ASP.NET page containing controls, including this COMET grid.
  • Step 3: COMET grid makes an AJAX style call to the server to get an update.
  • Step 4: The call is not completed by the server, but kept pending.
  • Step 5: Server waits for any server side events, such as a data update to any client or timeout.
  • Step 6: If an event occurs at the server side, a client update is sent. Other client calls are still kept pending.
  • Step 7: If a timeout occurs, all the client calls are completed.
  • Step 8: Continue from Step (3).

Let's look into the interesting parts in the above steps. The most interesting part is to keep the client's call pending. This is achieved by writing a handler based on IHttpAsyncHandler. All we need to do is to override three methods: BeginProcessRequest, EndProcessRequest, and ProcessRequest. We are not doing anything in ProcessRequest. In BeginProcessRequest, all we do is: put the call in pending state. This is an important step, because this returns the ASP.NET thread to the thread pool, freeing up the server's resource. Hence, the same thread can be used to service any further connecting clients. The code is shown below:

C#
public IAsyncResult BeginProcessRequest(HttpContext Context, 
                    AsyncCallback Callback, object ExtraData)
{
   string ClientId = Context.ApplicationInstance.Request.QueryString[0]; //Line 1
   string SessionId = Context.ApplicationInstance.Request.QueryString[1]; // Line 2
   ResponseDetails respDet = clientDetails.GetFirstItem(ClientId);   // Line 3

   GridAsynchResult asyncResultToAdd = new GridAsynchResult(SessionId, respDet, 
                    Context, Callback, ClientStatus.Updated); // Line 4
   respDet.AddClient(asyncResultToAdd); // Line 5

   return asyncResultToAdd; // Line 6
}

In the above code, the first line gets the clientId. This is the ID of the grid control. If you have two grid controls, you will have two different clientIds to represent them. The second line takes the session ID, a string that distinguishes each client. Line 3 retrieves the response details already stored for this client. The response details class stores the connected client queue and the grid control's instance. This information is added to a newly created AsynchResult instance. The AsynchResult is returned to the caller. As mentioned above, this method frees up the ASP.NET thread, which enables more number of clients to be connected concurrently.

When a server side event happens requiring a data change in the grid, the AsynchResult's SetCompleted method is called. Note that the server-side event is triggered by the user of this control. I.e., if you are using this control, you would have called the GridInstanceName.DynUpdateClient() method to trigger this server side event. To work for your call, the SetCompleted method is called on the AsynchResult. This results in calling EndProcessRequest. Again, all we do in this method is write the XML response to the client (which made the AJAX style call). This completes a call for the client. The code for EndProcessRequest is as shown below:

C#
public void EndProcessRequest(IAsyncResult result)
{
   GridAsynchResult asynchResult = (GridAsynchResult)result; // Line 1

   asynchResult.ClientContext.Response.ContentType = "text/xml"; // Line 2
   asynchResult.ClientContext.Response.Write(
     asynchResult.RespDetails.GridCtrlAjaxInstance.GetXmlFeed(
                  asynchResult.SessionId)); // Line 3
   
}

Line 1 gets the parameter passed. The response type is marked as 'xml' in Line 2. Line 3 gets the XML response and writes to the Response object. Line 4 completes a call from the client.

Points of interest

  • This multi-client COMET control has the capability to update specific clients while conserving resources. This controls avoids several unnecessary AJAX calls based on timers from multiple clients.
  • Thread-safe: this multi-client COMET grid is thread safe. You can call the 'Dyn' prefixed methods from multiple threads. I.e., one of your threads may be updating the values, another may call DynUpdateClient to send an update to the specific client.
  • This control not only supports dynamically updating a specific client's (web browser's) grid data, but also supports dynamically changing the background color, text color, and background image.

Important reminder!

I would like to hear from you guys on your feedback. I would love to hear your detailed feedback, even if you vote 1. So, please do write your messages. Thanks!

References

  1. http://msdn.microsoft.com/en-us/library/system.web.ihttpasynchandler.aspx
  2. http://msdn.microsoft.com/en-us/library/system.web.ihttpasynchandler.beginprocessrequest.aspx
  3. http://msdn.microsoft.com/en-us/library/system.web.ihttpasynchandler.endprocessrequest.aspx
  4. http://en.wikipedia.org/wiki/XMLHttpRequest
  5. http://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx

History

  • 3 Apr. 2009 - First version.

License

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