I have a service that performs a slow task, when its finished I want to update the client using AJAX with the result of the task. In my client I call the task many times to update a grid of results. For the purposes of understanding, its a connection tester that loops through a list of connections to see if they are alive.
I have implemented the service as WCF. I generate async methods when I add the service reference to my web client.
The code works fine, however the screen locks momentarily when the callbacks fire – I think this is because they all happen one after the other and all of them repaint the GridView within quick succession.
I don’t want this glitch to happen -I was hoping the AJAX implementation would be able to partially update the GridView as results come back from the service via the callbacks.
The only way I can make this look nice is to launch the async calls in a separate client thread and then use a timer to repaint the data to the grid (the same data that’s being updated in the separate thread via the callbacks).
I’m doing this mini-project as a learning exercise then I aim to do the same with MVC3 to know the differences.
Code Snippet (without separate thread, causing screen rendering to slow down during callback):
//get list of connections from session
ConnectionList myConns = Session[SESSION_ID] as ConnectionList;
//pass into async service call
GetAllStatusAsync(myConns);
protected void GetAllStatusAsync(ConnectionList myConns)
{
Service1Client myClient = new WcfConnectionServiceRef.Service1Client();
myClient.AsyncWorkCompleted += new EventHandler<AsyncWorkCompletedEventArgs>(myClient_AsyncWorkCompleted);
foreach (ConnectionDetail conn in myConns.ConnectionDetail)
{
//this call isnt blocking, conn wont be updated until later in the callback
myClient.AsyncWorkAsync(conn);
}
}
//callback method from async task
void myClient_AsyncWorkCompleted(object sender, AsyncWorkCompletedEventArgs e)
{
ConnectionDetail connResult = e.Result;
//get list of connections from session
ConnectionList myConns = Session[SESSION_ID] as ConnectionList;
//update our local store
UpdateConnectionStore(connResult, myConns);
//rebind grid
BindConnectionDetailsToGrid(myConns);
}
The question is – can this be done in a better way in asp.net / AJAX? (To avoid the rendering lock up problems and get the grid partially updating as results come in) I don’t really want to use a separate client thread such as the following snippet:
// Perform processing of files async in another thread so rendering is not slowed down
// this is a fire and forget approach so i will never get results back unless i poll for them in timer from the main thread
ThreadPool.QueueUserWorkItem(delegate
{
//get list of connections from session
ConnectionList myConns = Session[SESSION_ID] as ConnectionList;
//pass into async service call
GetAllStatusAsync(myConns);
});
UPDATE:
Adding Page Markup as requested:
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ASForm.aspx.cs" Inherits="Web_Asp_FBMonitor.ASForm" Async="true" EnableSessionState="True" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
ASP.NET Connection Test (Client in ASYNC, Server in ASYNC)
</h2>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<p>
<%--This update panel shows the time, updated every second--%>
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<h3> <asp:Label ID="LabelTime" runat="server" Text=""></asp:Label> </h3>
<asp:Timer ID="Timer1" runat="server" Interval="1000" ontick="Timer1_Tick"> </asp:Timer>
</ContentTemplate>
</asp:UpdatePanel>
</p>
<p>
<%--This update panel shows our results grid--%>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Client Sync Page</asp:HyperLink>
<br />
<asp:Button ID="ButtonUpdate" runat="server" Text="Update"
onclick="ButtonUpdate_Click" />
</ContentTemplate>
</asp:UpdatePanel>
</p>
</asp:Content>
UPDATE 2:
I’m looking for a concise client side JS example of this. The received options are good and have been greatly appreciated but the client side JS is the one I am stuggling with through my own inexperience and will be awarding the bounty for this.
You see what you call “screen locks” because of the ASP
UpdatePanels.ASP.NET WebForms are an attempt to make the web act like Windows forms. Admirable? Depends on who you ask.
When you use an
UpdatePanel, ASP.NET stores the server-side controls inViewState, makes any changes to that ViewState that is necessary (in your case, it’s updating the page based on code in yourTimer_TickandButtonUpdate_Clickfunctions). Then, it refreshes the page with this new data, causing the screen locks you describe.To get around this, you’ll have to use real AJAX. A lot of people do this with jQuery AJAX functions and one of the following options:
There are quite a few questions here on SO about hooking ASP.NET WebMethods up via jQuery, and some about loading ASP.NET pages, but not as many questions about AJAX and WCF.
If you choose to use AJAX and jQuery, your resulting page would look something like this:
Then, on a separate page (I arbitrarily called it
GridViewResults.aspxabove), you’d have: