Wednesday, January 30, 2008

Mind your callbacks

These days I'm using quite a lot of Callbacks, that is requests to the server that don't refresh the entire page, but only a specific section, creating the illusion of a snappier user interface. As such, they are becoming a necessity in these days of Users 2.0 (ie users who have gotten used to Web 2.0 functionalities, much as I loathe the term).

Google were one of the main early adopters of callbacks, but now everyone is doing it, and so are the main out-of-the-box software vendors such as Telerik or ComponentArt. These days the challenge would be to find a grid that does not implement the latest AJAX-style gadgets.

Our 3rd party vendor of choice is currently ComponentArt, and I've been using their Callback mechanisms quite extensively lately. They're quite good, although sometimes its still necessary to go down to the nitty-gritty of client-side scripting, which is not for the faint of hearted.

Of course, while callbacks are great, they tend to have a big impact on they way your applications are constructed, amongst others interfering with what we consider "best practices", such as:

* Strong Typing (hello Mr JavaScript)
* Error Handling (an alert window does not make an error handler)
* Intellisense (maybe not a best practice, but damn handy to have anyway)

Anyway, in the end its just (yet) another technology paradigma shift and most of those "best practice" challenges can be solved decently enough, given a little time and effort. Lets take error handling as an example:

Problem: When an error occurs during a Callback, the typical error message is "Data cannot be loaded", delivered in the form of a javascript alert. Not too useful, not to mention the fact that your page is potentially left in a compromised state.

Solution: In the end, the Callback method is just another server-side method, so encapsulate it in an try/catch block and provide adequate error handling. After that, generate some client-side script to redirect the user away from the page to some nice error page.

protected void MyCallBack(object sender, CallBackEventArgs e)
{
try
{
BindGrid();
panel.RenderControl(e.Output);
}
catch(Exception ex)
{

new ErrorReporter().HandleError(Request, ex);
CallBackErrorHandler.RedirectToErrorPage("Error.aspx", panel, e.Output);
}
}


and then in our CallBackErrorHandler class:

public static class CallBackErrorHandler
{
public static void RedirectToErrorPage(string redirectUrl, Control container, HtmlTextWriter output)
{
string str = "<script type='text/javascript'>";
str += "window.location = '" + redirectUrl + "'</script>";
Literal lit = new Literal();
lit.Text = str;
container.Controls.Add(lit);
container.RenderControl(output);
}
}


Old tricks, new techology :)

Monday, January 21, 2008

Dynamic Services

Something useful I recently learned:

While you can without any problem attach parameters to a .NET Windows Service (by means of a configuration file), how the heck am I supposed to enter parameters into the INSTALL program itself (such as, the service name...).

The answer was simple: InstallUtil.exe (the installer program) can actually be called with command-line parameters appended to it. These can then easily be processed in the installer class itself.

installutil /DisplayName=Sam " WindowsService1.exe"


And then in your installer.cs file:

protected override void OnBeforeInstall(IDictionary savedState)
{
base.OnBeforeInstall(savedState);
string name = Context.Parameters["DisplayName"];
if (name != null)
serviceInstaller1.ServiceName = name;
}


Take care: this needs to happen in your OnBeforeInstall handler, after that your Context will be NULL!

Thursday, January 17, 2008

WebExceptions and you

Every now and then, one of my applications was throwing up an error of type 'System.Net.WebException: The request was aborted: The request was canceled'.

This happened during execution of a SOAP webservice call towards a backend system. Problem was, it was almost impossible to reproduce, happening as it did on irregular, non-deterministic moments. I only noticed it because of an automatic error reporter, which automatically mails any exceptions to a public Outlook folder.

After finding some time to investigate the issue, I found that it was apparently a known issue, related to the Keep-Alive property of the HTTP Request itself. Apparently, every now and then, IIS ate the webservice request, when Keep-Alive was true.

One possible solution was of course to disable Keep-Alive on the IIS level. While this would no doubt solve the problem, it is kinda overkill and can have negative implications on the performance of the application as a whole (Keep-Alive is there for a reason, you know).

A better solution is to disable Keep-Alive for the SOAP request only, and leave it on for the other requests. This was slightly complicated by the fact that - like most .NET users - we don't actually code our webservice consumption classes ourself, but use generated Proxy classes, obtained by either wsdl.exe or by adding a Web Reference in VS.NET. Luckily, by means of a simple subclass, we could dance around this problem and keep our code generation intact.


public class MyCustomService : MyGeneratedService
{
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
request.KeepAlive = false;

return request;
}
}


After introducing these changes for each SOAP request, the intermittent WebException has dissapeared. Life is good.