UpdatePanel Addiction

April 30, 2008

Update panel addiction is a nasty thing. Projects start, it’s decided they are to be “Ajaxified” – the answer: chuck in 50 million update panels to do the trick. The more the better is the general principal, as this means there are smaller sections of the page being updated. UpdatePanels are designed to be used to update small portions of the page – the less content encompassed by an UpdatePanel, the better.

With a sufficiently advanced application the need may arise to refresh update panels from other update panels by calling the Update() method.

One problem that can occur in such advanced applications (especially those which use some kind of framework for events and control instantiation and such) is that slowdowns can be caused when update panel refreshing is too liberal. Perhaps only a small section of the page should be being updated, but for some reason it’s parent UpdatePanel is also refreshing, or there are simply too many updates per post back. Slowdowns can also be caused when large amounts of content, like in an un-paged grid or table of data is sent back to the client for rendering… the delays are not only in the transmission of the data to client but also while the browser is rendering the new data (especially when tearing down the existing DOM elements, more on that in another post).

What ever the case, sometimes it’s nice to see what is going on under to hood during an async postback.

Ajax Response

The ASP.NET Ajax system sends back async updates as special tokenised text. E.g.

247|updatePanel|UpdatePanel1|
askdljfjlkasdf aklsdflkasfjlkadjkldas
<br /><br /><span id="Label1">29/04/2008 6:40:36 AM</span><br />
<input type="submit" name="Button1" value="Button" id="Button1" />
|128|hiddenField|__VIEWSTATE|/asdfasdf|48|hiddenField|__EVENTVALIDATION|/wEWAgLWzvmRBwKM54rGBpmraqo+tCbnoafT7bqYDaCZ2bH5|
0|asyncPostBackControlIDs|||0|postBackControlIDs|||13|updatePanelIDs||tUpdatePanel1|
0|childUpdatePanelIDs|||12|panelsToRefreshIDs||UpdatePanel1|2|asyncPostBackTimeout||90|12|formAction||Default.aspx|13|pageTitle||Untitled Page|

To view the tokenised output of your page, try something like the following code in your page:


protected override void Render(HtmlTextWriter writer)
{
   ScriptManager sm = ScriptManager.GetCurrent(this);
   if (sm == null || !sm.IsInAsyncPostBack)
   {
      //this is a normal postback
      base.Render(writer);
   }
   else
   {
      //this is an async post back (partial render) so lets see what is going to be output
      HtmlTextWriter textWrite = new HtmlTextWriter(new System.IO.StringWriter());
      base.Render(textWrite);
      //have a look at content variable after the next line has run
      string content = textWrite.InnerWriter.ToString();
      writer.Write(content);
   }
}

The basic breakdown of this output is: [SectionLength]|[TokenCommand]|[TargetID]|[Content]

As you can see there are various commands. Some of the more interesting ones are:

  • updatePanel – refreshes the target UpdatePanel with the new content. UpdatePanels are simply either DIVs or SPANs (depending on the the RenderMode attribute is set to Block or Inline) in the final rendered content – so this content will just overwrite the existing content.
  • hiddenField – update any field’s value with the new value here
  • expando – update any element’s attribute with a new value
  • pageTitle – updates the page title in the browser window
  • focus – after the render has completed this control will be given focus.

You can find lots more information and some cool tricks on Siderite Zackwehdex’s blog here: http://siderite.blogspot.com/2007/05/messing-with-updatepanel-to-speed-up.html.

Browser Add-ins

There are a number of ways to view the innards of a server round trip. For starters, every developer should have these tools: Firebug for Firefox and Web Development Helper for IE – you do test in at least these two browsers right?

Web Development Helper (Internet Explorer only) (by Nikhil Kothari of ASP.NET team fame) allows very close inspection of the AJAX response. Grab the installer from here: http://www.codeplex.com/webdevhelper.

Start the add-in by selecting View -> Explorer Bar -> Web Development Helper.

Fire up your latest AJAX based work of art, and select the “Enable Logging” checkbox. Perform an action that will kick off an UpdatePanel refresh, then double click the response in the logging window. Select the Response Content tab from the bottom section of the popup and voila – you have your AJAX information. Fold out updatePanel and select one of the items – you will see the content that was sent back to the client.

WebDevHelperSShot

Trace debugging UpdatePanel movements

In Visual Studio, it’s sometimes nice to write out some custom debugging information into the output window to get a broader view of what is happening with your program. Previously this has not been straight forward to achieve with client script (often JS debug sessions ended in “alert” style debugging) – but with the MS AJAX Client Library it’s a breeze.

Using the Web Development Helper add-in is a bit like bringing up a quick watch every time you want to view the information… but what if you want to survey an application over a period of time or you have a lot of action going on and want to see what’s happening without having to stop each time to bring up the debug results.

Luckily you can hook into the PageScriptManager object to receive notifications when the AJAX framework is doing things – then show some information about what is going on in the Visual Studio output window (and in the Script Console window in Web Development Helper).

Here is something I have put together for you to try.

First create a new JS file in your project and place the following code in it:


var updatePanelHook =
{
    initialised : false,
    init : function()
    {
        if (!this.initialised)
        {
            //hook up to the various page loading events to provide extended update panel functionality
            var prm = Sys.WebForms.PageRequestManager.getInstance();
            prm.add_pageLoading(pageLoadingHandler);
            this.initialised = true;
        }
        function pageLoadingHandler(sender, args)
        {
            var arr = args.get_panelsUpdating();
            for(var i =0; i< arr.length;i++)
            {
                var updatedPanel = arr[i];
                Sys.Debug.trace(String.format("Updated: {0}", updatedPanel.id));
            }
        }
    }
}

if (typeof(Sys) != 'undefined' )
{
    Sys.Application.notifyScriptLoaded();
}

Ensure the script is included in your page (or master page etc). It’s best to use the ScriptManager object to do this:


<asp:ScriptManager ID="ScriptManager1" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/JScript1.js" />
    </Scripts>
</asp:ScriptManager>

Place the following code in your page to kick off the init function:

<script type="text/javascript">
      updatePanelHook.init();
</script>

NOTE: The init() function checks that it has not already performed initiation, because the updatePanelHook variable and associated event subscriptions will persist though partial updates.

In Web Development Helper, switch to the Script Console view by clicking on HTTPLogging and selecting the option from the drop down.

Perform an action that causes an UpdatePanel post back and you will see the results of you postback in the script window. If you have run your project by debugging from Visual Studio, you will also see the results in the output window there. Slot that into a larger ASP.NET AJAX based app and it should assist you figuring out what your app is doing under the hood!

An interesting point here is that the objects in the “arr” variable in this code are DOM elements (DIVs and SPAN’s)… so you can do all sorts of things with them if you like.

This process helped me figure out why a large commercial web app I was working on was slowing down, and with some ingenuity I managed to get the number and size of the UpdatePanel requests right down, gaining more than a 2x speed improvement across the entire app.

One other small point: when performing HTTPLogging in Web Development Helper, keep an eye on the Response Size column… obviously the smaller you can get your responses the better.


Shortcuts on Windows Vista start menu do not work

April 30, 2008

If you have items on your Windows Vista start menu that do nothing when you click them – and you know they should be working – such as Word or other Office programs, then this may be your answer.

For starters, if you have KDiff3 installed then the answer to your problem may be simple – the problem is with KDiff3′s context menu handler – see http://sourceforge.net/tracker/index.php?func=detail&aid=1759117&group_id=58666&atid=488548.

If you do not have KDiff3, then you might want to look at point 12 on this page: http://windowsxp.mvps.org/slowrightclick.htm. Follow the instructions to use ShellExView until your shortcuts start working.

Big thanks to Rosso for taking the time last year to figure this out!


DTS-HD MA on PS3

April 19, 2008

Wow,

Just watched Die Hard 4 in DTS HD-MA… ON MY PS3!!

Thank you Sony at last!!

DTS-MA in operation

Sorry for the quality of the pic, my GF has my camera out on the town and I had to take it on my TYTNII Windows Mobile device.

A previous post detailed my trials and tribulations about Dolby TrueHD and DTS-MA (http://jakkaj.wordpress.com/2008/03/01/dolby-truehd-and-dts-hd-ma-on-ps3/)… looks like the latest PS3 update (2.30) has added DTS-MA decoding to the PS3 OS. Or 2.20, I dunno, I stopped checking every update for DTS-MA a few months back:)

Suffice it to say, DTS-MA is here! To enable, check out my post listed above – but remember, if you want to show off to your friends your cool new amp in its true DTS-MA and Dolby TrueHD glory, ensure they concentrate on the sound… your amp will not show that it’s decoding this awesome format (you will only see PCM, which comes across as being something you would see in a  Windows 95 WAV at first, until you realise that PCM means pure, un-adulterated lossless multi-channel awesomeness). In short, the Sony decodes the audio internally and sends it in a raw, lossless format to your amp – same audio, but no DTS-MA HD light on your amp:)

AMP: Yamaha RX-V3800
Speaker Setup: Klipsche F-3 front, C2 centre and F1 surround.
Awesomeness: 100%

“Yeah I saw it! I did it!!”


Lucene Wrapper Project

April 4, 2008

Chapter One: Life Before Lucene

A large part of the Objectify CMS (well any good CMS) is it’s search capabilities. This is compounded by the fact that the Objectify CMS includes a powerful Knowledge Management System (KMS) component as well.

A few years ago I wrote my first ever search engine… it was a powerful, full text indexing, stemming, soundexing (sic) fully custom built indexing system for use with the Objectify CMS and KMS. It worked a treat, was fast, worked with the CMS’s HTML content and its XML based Knowledge Base content… but it had a few flaws. It’s query syntax was limited, it was complex and hard to maintain – and it didn’t scale over about 10,000 records (paltry??)… the memory requirements started scaling with it.

Building this search engine was a long a arduous task, with my gotchas, pitfalls and hurdles along the way. Performance and scalability where perhaps the largest and most difficult issue to deal with (err… sounds like any IT project to me – ed). Long story short the system worked well, but the time came to add a raft of new features and address the scalability concerns. The query syntax needed a few tweaks also.

Chapter Two: My new love – Lucene

Oh Lucene how I love thee. The decision was hard, but I decided it would be best to fully re-write the search engine in Lucene – boy am I glad I did.

Chapter Three: The first date

It took me a while to sort out the best way to manage documents in Lucene, all the options, the patterns and practices, and how to integrate it into the Objectify CMS and KMS products.

Basically, Lucene is built to be really generic – it can work in just about any situation you need a search, from Windows forms applications, web applications, services you name it. And it can scale… and scale and scale… 13 million records on a moderately equipped machine is nothing to Lucene… and it can handle multiple gigabyte indexes with ease. Long story short it’s a set of search engine base classes, certainly not a fully fledged search engine – it’s up to the developer to implement all the features… which is probably why you are reading this – you want full control.

Chapter Four: Third base

With all this in mind, I thought I would put together a little class library that you can use to create and search with. It’s really quite simple… just grab the code, grab Lucene.NET and build away!

My aim with this two project is to hit one of two goals:

1) You take this project and use it in your own projects with none/little change and it helps out immensely with your project deadlines

2) You take this project, pull it apart and learn all about how to implement Lucene.NET by example – cutting down the time it takes to get your own test projects up and running.

Chapter Five: How to use this project

Using this project is quite straight forward. Look in the lJak Tests project for a test class which should have all the information you require.

Lucene.NET and other search engines work great with TDD (Test Driven Development) so use the tests to your advantage as you develop – it will save you piles of time.

The basics are:

1. Choose an ID for your document. Something like the file name of the source page, a GUID or the ID of the piece of content from your database. You will use this ID later to refer back to the original document from the search results.

2. Split up your document/parse into logical blocks. You may want to split a HTML page into areas like Title, Heading, and each P/DIV tag or something. That way the end user could search on just title, or just body etc.

3. Parse apart the content. This code does not include a parser. So, you have to break the content up yourself. It would be a good idea to strip out HTML etc also. For the CMS/KMS products I wrote a parser that takes the content XML and breaks it up based on tag name, id and whether it’s and attribute or child node etc. If i get some time I may post a sample parser.

4. Instantiate the LuceneDocument complete with your ID and a path to your index.

string id = "somefile.aspx";
string indexName = "TestIndex";
LuceneUpdateDocument target = new LuceneUpdateDocument(id, indexName);

The index will be automatically created in the loading assemblies directory\index. You can change this behaviour in:


public static IndexWriter GetIndexWriter(string name)

...

DirectoryInfo indexBase = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "\\Index\\" + name);

...

Note: Hrm, I must have been tired when I wrote that, should have used String.Format.

5. Add fields to the document. These are what will actually be searched. You can choose to add the field so they can be searched and read back from the index, or you can add them so they can be searched but not read back from the index- use the first option for attributes like filename, date and other small pices of metadata, and use the second option for the main bulk of content. Your index will be too big if you try to store everything in there – so ensure your main content is indexed but not stored (thus not read back).To index attributes:


target.AddField("Title", "This is a test document", LuceneUpdateDocument.FieldStoreType.Attribute);

To index main content areas:


target.AddField("Content", "This is the main body content", LuceneUpdateDocument.FieldStoreType.Content);

6. Commit the new items to the index. This indexes the content and commits it to the index. It also clears out some internal caching that the wrapper class performs.

target.Commit();
Index.ClearCache();

7. Search your new index! Searching is easy!


//Create an instance of the searching class, passing in the same index that you used to add the content
Search s = new Search(indexName);
//This line searches for the word "document" in the Title and Content fields.
List<SearchResult> results = s.QueryBasic("document", "Title", "Content");

//This line searches for the word "Main" in the Title and Content fields.
results = s.QueryBasic("Main", "Title", "Content");

foreach (SearchResult sr in results)
{
     string title = sr.GetFieldVal("Title");
     Assert.AreEqual(title, "This is a test document");
}

As you can see above, to search the index the syntax is

 s.QueryBasic("Some Query", "FieldOne", "FieldTwo", ..., ..., "FieldX");

The first parameter is the search query, you can look up all the Lucene query syntax here. After the first parameter you can list as many search fields as you like using the parameter array. Only the fields you list here (or any fields you specify in the query using special syntax) will be scanned by Lucene.

8. Reading the content from the index. Searching isn’t much good unless you can read information from your search results! Reading your attributes is easy, because they are searchable and stored in the index for quick retrieval (remember above how some items where marked as attributes and stored whereas other items are marked as main content – thus not stored). Note in the code sample above there is a foreach statement which iterates over each search result. In this foreach it pulls out the title field and performs an assert on it.

string title = sr.GetFieldVal("Title");

It’s too easy to get the stored attributes. But how about main content? Well this is up to you. Using the ID that you stored earlier (get it out like the title attribute above) load the original document and read what you need – or just redirect the user to it to view. Review the Lucene query documentation for a full list of all the cool stuff you can do!

Chapter Six: That’s a lovely story, but I don’t give a crap – where is the code man??

Firstly you will need to get the Lucene.NET DLL from here http://incubator.apache.org/lucene.net/. I didn’t include it in the download as I wasn’t sure of the legality – and I couldn’t be bothered reading their re-distribution policy.

LJAK – Lucene.NET Wrapper Class (Source).

Documentation (haha just kidding, like there is documentation).

Legal – Please read legal stuff at the top of the LuceneDocument class.

Happy indexing and searching!