Saturday, June 14, 2008

A look at canceling a page method call.

Last year I did some posts on ASP.Net AJAX and how to use it.  The posts were based on my experiences of using it up to that point.  Since then, I've been busy on a lot of different project and am finally circling back around to ASP.Net AJAX. 

I ran into an issue a couple weeks ago, now, that caused more research time than I was hoping for.  The scenario was simple. A user types in an ID into a removal form that would, in essence, remove the record from the database.  Instead of blindly "deleting" the record, the new update was to query the record and provide information about it into a new confirmation-like window.  At that time the user can commit the removal or opt to not do it.  While querying and processing, a "cancel" button was to be placed to prevent the current action from taking place.

This sounds simple enough.  In order to boost page performance (the page is already littered with legacy practices that need to be cleaned up), I opted to implement page methods to do the actions instead of Update Panels.  This made a noticeable difference; however, it raised the question of how do I cancel an asynchronous page method call.

After a handful more hours than I had hoped, I still couldn't find the answer already posted.  Then I finally located this post that was doing what I wanted using Web Services.  In it, Kazi Manzur Rashid Amit, does a great job in explaining the technique required cancel a web service call.  Since Enabling Page Methods cause the page's public static methods to be exposed as web methods, I thought they were the same; however, there seems to be a slight difference which I'll mention after the I through the process.

First thing's first.  If you are interested on how to setup Page Methods, please refer to my Introduction to Page Methods Post for instructions on how to begin working with them.  The method described there is very straight forward and works well for a lot of situations.  In order to call any page method from the JavaScript code, you simply have to add a line like the following:

PageMethods.myStaticPageMethod(param, onSuccess, onFailure)

This is very simple.  The "myStaticPageMethod" is the name of the page method in the code behind file and accepts a parameter, identified by "param".  If the method call is successful, the JavaScript method "onSuccess" will be called while "onFailure" will be called if it wasn't successful.  Again, this is very simple.

Now, where the issue comes in, which Amit's blog points out, is that this "myStaticPageMethod" syntax is references a void method (or a method that doesn't return any value or object).  While the page method's code may pass a value into the "onSuccess" or "onFailure" methods, the actual page method call returns no value. In order to have the method return a value, or to be more specific the instance of the request object, we need to change the syntax slightly.

PageMethods._staticInstance.myStaticPageMethod(param, onSuccess, onFailure)

This snippet is identical to the previous one with the exception that we've added the "_staticInstance" piece between the object name and method name.  Like web service calls, when the ASP.Net AJAX Script resources create the JavaScript object prototypes for Page Methods that get passed to the client, they also create static instances of the object that return the request object.  This request object can then be set to a variable and used to cancel the call if required.

Since my requirement was to have a cancel button during processing, I had to use the static instance version of the page method.  In addition, I set the method call's request to a global JavaScript variable so that it could be references by other functions (i.e. the button click event).  So up to this point, my code looks something like this:

var request; //outside of any function definition
request = PageMethods._staticInstance.myStaticPageMethod(param, onSuccess, onFailure) //inside my functions

Now, that I have the request object assigned, I need to cancel the request.  To do this, Amit's blog pointed me to the WebRequestExecutor object.  Microsoft's documentation on the WebRequestExecutor object can be found here.  This object allows for web requests to be queried and acted upon.  For our goal, we want to utilize its "abort()" method; however, we first have to get our request's executor.  To do this, we call the request's "get_executor()" method.

var executor = request.get_executor();

After we have the executor object, we have to ensure that it's still started.  The WebRequestExecutor has a started property and to access it's value, the API has a "get_started()" method.  If the method returns true, then the executor has started processing our web request else it hasn't yet or has already finished.  Once we have identified that the executor has started processing our request, we can then abort it.  Below is the code snippet that illustrates this.

if (executor.get_started())
{
    executor.abort();
}

Here's where I saw a difference between my method and the code posted on Amit's blog.  When I called the abort method, my "onFailure" method was being called and providing the error message "The server method 'myStaticPageMethod' failed."  When I ran Amit's code which uses Web Services, I didn't see this error.  I haven't looked through the code thoroughly to see if the error is being hidden or just not bubbling so I there may not be a true difference here.  Regardless, I ended up throwing together a small "hack" to prevent the error for being shown to the end user each time a request was canceled.

Since I could not prevent the error, I ended up having to hide it.  I don't like this solution; however, I haven't been able to find a better one.  If anyone knows of a better, cleaner way please let me know.  In order to hide the error, I declared another global variable, similar to the request variable of these examples.  I named the variable "aborted" since it would signify whether or not the request has been or not by my code.  Afterwards, I just assigned it the boolean value of true before I abort the call.

var aborted = false; //placed outside of any function

//modified aborted snippet
if (executor.get_started())
{
    aborted = true;
    executor.abort();
}

Now that I had an indicator when a user attempted to manually abort a method call, I could add the following into my "onFailure" function to prevent the error from appearing.

if (aborted == true)
{
    aborted = false;
    return;
}

So, after all of that, we have done the following to add the functionality of canceling a Page Method to our code:

  1. Declared the "request" global variable
  2. Declared the "aborted" global variable
  3. Set the "request" variable to the WebRequest object returned by the static instance version of our page method
  4. Set the "request" variable = null at the beginning of each method to prevent confusion
  5. Created an "executor" variable in our cancel request method.
  6. Set the "executor" variable to the WebRequest's executor property value
  7. Ensured the "executor" variable has started processing our request
  8. If started, set the "aborted" variable to true
  9. If started, called the "executor" variable's "abort()" method
  10. Stopped the failure method from fully processing the error by returning if the request was aborted.

That's a lot of steps; however, there may be a better way.  Again, if there is an overall better way or at least a better way to prevent that error message when aborting a PageMethod request, please let me know.


kick it on DotNetKicks.com

Sunday, June 8, 2008

A look ahead

It has been too long since I have posted. While this is not a post about technology directly, it is a post about this blog.

In the past, I have semi-held true to the title of Random Coding. This will remain true and yet not in the coming weeks/months. There are a lot of focus that I'll be giving this blog from a UI perspective as well as the content with in.

The following posts will be making their way onto here soon:

  • Canceling MS AJAX Page Method calls

  • Exploring/Learning WCF and WF

  • MCTS topics of interest



A few weeks ago I attempted to find out how to cancel web method calls in ASP.Net Ajax and I couldn't find it. I ended up locating a post on canceling web service calls and was able to modify the code appropriately for my purposes. When finished though, it still threw me a curve ball that I had to address.

For over a year now, I've been learning WCF and WF off and on. While I have been learning it, I've done very VERY little with it. I'm going to be creating a number of posts based around a pet project that will utilize both of these.

From what I can tell, my MCAD can no longer be upgraded to a MCPD certification now that the 2008 tracks are out. I'm looking at my procrastination with the light that I'll be able to relearn the necessary information to pass the new certification tests. This is good since even though I do my day job well, it sometimes cause me to specialize more than I want. As I work on getting my TS and eventually PD certification(s), I'll be posting any tidbits, tips and tricks, and pet projects through here to help others as well.