The current project I am working on had a problem. The mobile users who were accessing the app were getting server 500 errors. But not from us, from the carrier’s proxy server. It seems our requests were taking too long, and the carrier’s proxy was just giving up.
The only way to remedy this was to keep the whole session alive by doing the Expedia type refresh that sends a page to the user saying “Your request is processing”, waits 5 seconds, and makes a request to the server. The server checks to see if the request is finished, if not, it sends out another “Processing” page, and if it is, it shows the results. Simple, right?
For a desktop application, it would be. Just kick the request off in another thread, register a callback to know when the thread is done, and continue on your merry way. But how do you do it with a web form, in a resuable way?
I first kicked around using a session variable to track the request, and a callback that would set this session variable:
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
if(Request.QueryString["load"] != null)
{
if(Session["requestObject"] == null)
{
RequestObject obj = (ResultObject)Session["requestObject"];
DisplayResult(obj);
}
else
{
WriteLoadingCard();
}
}
}
}
private void cmdSubmit_Click(object sender, EventArgs e)
{
RequestObject req = new RequestObject(ID.Text, Name.Text);
RequestHandler handler = new RequestHandler(req);
handler.ExecutionComplete +=
new ExecutionCompleteDelegate(LoadComplete);
ThreadStartt ts = new ThreadStart(handler.Load);
Thread thread = new Thread(ts);
thread.Start();
}
private void LoadComplete(object sender, EventArgs e)
{
RequestHandler handler = (RequestHandler)sender;
Session["requestObject"] = handler.RequestObject;
}
Which just seemed like too much work, and tied way to much business logic in the UI, right where I don’t want it. I needed this to work in many, many places, but wanted to modify as little code as possible, and of course wanted it to be as resuable as possible.
The stickler is that the app has to be able to either give an object back to a page, or store it somewhere the page can get to it. I didn’t particularly like the idea of tying myself to Session variables, and in fact part of the requirements were that we not tie ourselves to session variables.
Since what I needed to do was find an object, I started thinking about a Registry
Registry: A well-known object that other objects can use to find common objects and services.
So if I could find a way to create a repeatable, resuable way to check for objects in the registry, and load them into the registry when they were done loading, I could use that as the pass through. The sketch in my mind looked like:
FindObject(params) <- Will always return an object
if object is loaded
use object
otherwise
tell object to register itself when done loading
load object
The first thing I needed was an object that knew how to load itself:
public class RequestObject
{
public void Load()
{
if(!isLoading)
{
isLoaded = false;
isLoading = true;
RequestHandler handler =
new RequestHandler(this);
handler.LoadComplete +=
new LoadCompleteDelegate(LoadComplete);
ThreadStart ts = new ThreadStart(handler.Load);
Thread thread = new Thread(ts);
thread.Start();
}
}
public void LoadComplete(object sender, EventArgs e)
{
RequestHandler handler = (RequestHandler)sender;
RequestObject result = handler.RequestObject;
result.IsLoaded = true;
result.IsLoading = false;
RequestObjectRegistry.Register(result);
}
}
Now I needed the registry the objects could use to register themselves and other objects could find them at. In this case I used the Application Cache, but I could modify that to be a session or even a static hashtable.
public class RequestObjectRegistry
{
public static void Find(RequestObject obj)
{
string cacheName = obj.ID.ToString();
Cache cache = System.Web.HttpContext.Current.Cache;
if(cache[cacheName] == null)
{
Register(obj);
return obj;
}
else
{
return (RequestObject)cache[cacheName];
}
}
public static void Register(RequestObject obj)
{
string cacheName = obj.ID.ToString();
Cache cache = System.Web.HttpContext.Current.Cache;
//overwrite any existing objects
cache[cacheName] = obj;
}
}
Finally I needed to make use of all this in my web and WAP pages. Because we use .NET’s code-behind pages, it’s fairly straightforward to have the logic be shared by the HTML and WAP versions of the page.
protected void Load_Page(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
if(Request.QueryString["ID"] != null && Request.QueryString["Name"] != null)
{
DisplayResult(Request.QueryString["ID"], Request.QueryString["Name"]);
}
}
}
private void cmdSubmit_Click(object sender, EventArgs e)
{
DisplayResult(ID.Text, Name.Text);
}
private void DisplayResult(string id, string name)
{
RequestObject obj = new RequestObject(id, name);
obj = RequestObjectRegistry.Find(obj);
if(obj.IsLoaded)
{
DisplayResult(obj);
}
else
{
WriteLoadingCard(id, name);
}
}
All in all it works very well, and is highly reuable for us.
Hi,
I tried to add image but I don’t know how to do this
Can anyone be kind to tell me how?
thanks a lot