Mehfuz's WebLog

Live crazy, think different!

Sponsors

News

Passionate about cutting edge technologies and facinated by the modern web and phone revolution.Currently working at Telerik Corporation, the leading .net component vendor.
Follow me


Articles


Projects

June 2008 - Posts

Implement Master-Detail layout with ASP.NET MVC

This is the first of posts that I am making to show out the things you can do with ASP.NET MVC. Also, it shows what I have done while building FlickrXplorer. I am bit lazy to write one article for it, so I thought rather to start it here.

In this post, I will show how you can implement a master-detail layout that invokes MVC controller to process its data and uses Ajax to do it in a non postback manner.

If you have looked though FlickrXplorer, you must have noticed that every list of images is tied with a detail view in a way that if user performs any action on the list ("click") , the detail view is updated accordingly.

Let's see the action flow below

image

It is almost clear that when you select an image it calls a Controller method or an Action method to be more precise. In this case, let's say that it is /Photo/Detail/112233. To map this a simple line in global.asax follows

routes.MapRoute("Detail", "Photo/Detail/{photoId}",
 new { controller = "Photo", action = "Detail" });

 

This will result in the call of PhotoController.Detail, when user hits the above url. Inside detail view its pretty simple.

[FilterResponse]
public ActionResult Detail(string photoId)
{
    try
    {
        PhotoDetail detail = model.GetPhoto(photoId); 
        return View(detail);
    }
    catch (Exception ex)
    {
        return View("Error", 
                    new ControllerException
      { ErrorUrl = HttpContext.Request.RawUrl, Message = ex.Message });
    }
}

As, expected it calls the model to get the photo and returns the ViewResult. Here, I haven't done any content related processing rather made a ViewPage named Detail.aspx which the MVC framework calls. By default, MVC framework looks for the ViewPage or ViewControl with name equals to the Action name, but View has other overrides that let me call other ViewPage/ViewControl as well. This is what done above when any error occurs, which calls Shared/Error.aspx. It is to be noted that if you have multiple controller and you want to access the same view from different controller, then you need to place it in the Shared folder, which will make it accessible by all controllers.

In the snippet, we also see that there is a FilterResponse attribute that will cache the response for same parameter set once the first call is made. FlickrResponse is inherited from MVC.ActionFilterAttribute attribute that provides a way to do some custom actions like caching and compression of http content.

So far, I have mapped the url and added the action method. Now, its time to get the result and show them in UI but I don't want any postbacks or redirects for it. So, I have used a callback model that did the work for me.

function renderContent(elementId, loadElementId, url, callback) {
    var element = $get(elementId);
    $get(loadElementId).style.display = "block";

    // Create the WebRequest object.
    var wRequest = new Sys.Net.WebRequest();

    // Set the request Url.  
    wRequest.set_url(url);
    // Set the request verb.
    wRequest.set_httpVerb("GET");
    // Set the Completed event handler, 
    // for processing return data
    wRequest.add_completed(function(sender, eventArgs) 
    {
        if (sender.get_responseAvailable()) 
        {
            element.innerHTML = sender.get_responseData();
            $get(loadElementId).style.display = "none";
            
            if (typeof callback != 'undefined')
                callback();
            
        }
    });
    // Make the request.
    wRequest.invoke();
}

 

Here, we see that renderContent takes in the Id of the element that will contain the rendered content and loading element that will be visible while the content is being processed. Optionally, we can pass in a parameter-less void callback which can be invoked after the content is processed. In that case, we might like to start the loading of comments after we have rendered the detail page. This renderContent is invoked while a user selects a photo from the list, this is a bit generic method which can be used for any callback scenarios. Therefore, for rendering photo detail, I wrapped it around so that it takes only a photoId.

function renderDeail(photoId, ignoreHash) {
    if (ignoreHash == 0) {
        if (window.location.hash == '') {
            // take the default one 
            window.location.hash = photoId;
        }
        else {
            photoId = getPhotoIdFromHash();
        }
    }
    else {
        window.location.hash = photoId;
    }
    var url = '<%= Html.ActionUrl("Photo", "Detail", new { photoId = "{0}" }) %>';
    url = String.format(unescape(url), photoId);
    renderContent("detailView", "loadingView", url, loadComments);
}

renderDetail  takes a photoId and ignoreHash := true/false. Inside, it uses Html.ActionUrl to process the url and then formats it with photoId and finally calls the renderContent. Html.ActionUrl is an extension method which is coded by me, the original is Html.ActionLink. So please dont get confused :-). I could have passed the url by hand but using ActionUrl  it creates the url on basis of the route mapping in global.asax. Also, for Photo controller if I change the url from /photo/detail/id to /p/detail/id, then I don't have to go everywhere in order to change the references.

Finally, while loading photo from url, I use window.hash to navigate to the selected photo, which is updated when a photo is clicked to show and thus, I have the master-detail layout yet url copy-paste facility.

That's it for now, I leave it on to the reader to explore it more in Codeplex. Also, hope that the introduction helps. Please note that some of the features like 2nd step loading of comments will be supported from 1.2 release (please check it out), so the JS might slightly differ to what is shown here.

Check it live at flickrmvc.net - send me feedbacks and updates.

kick it on DotNetKicks.com

Replace SortedDictionary with LINQ query - Part 2 (with comparison)

In my previous post, I have said that using LINQ query instead of SortedDictionary not only could be useful , elegant but also less processor intensive. In this post, I will show you a real comparison between the same the method but one with SortedDictionary and other with LINQ orderby query. I wrote a simple console application that mimics the action of GetSignature in LINQ.Flickr. Here, I will focus only on the sorting part that's why I removed the hashing and initialization of the method.

So, lets assume there are 10 url pairs that we need to sort for getting the signature. So, we need to create an array of 8 items and the processing method inserts another 2 (api_key and method name). Finally, when I run the two routines and took a snapshot from jetbrain's dottrace , I saw the following

comparisonInit 

Now, you can see that there are two methods TestSortedItemsWithSortedDic(SortedDictionary) and TestSortedItems (LINQ). The difference is almost 3 times the LINQ orderby query. Expanding the TestSortedItemsWithSortedDic will reveal the following

sorted

As you can see that adding items into sorted dictionary takes around 6.37% of the CPU cycle which is definitely because of the sort algorithm that it runs on each add. Also, there is another 1.39% during the initialization. Only these two items take up 7.76%, let alone the other SortedDictionary entries. Now, lets see how the processing with LINQ orderby looks like

LINQ 

LINQ iteration takes up 1.77%(where the original execution take place) , Dictionary.Add takes 0.54% comparing to 6.37% with sorted dictionary and additional orderby and select take 0.17 and 0.14% respectively. So, in total add and sorting takes up to (1.77 + 0.54 + 0.17 + 0.14) 3.39% of CPU cycles. Here to include that, highest CPU index may vary with current load, but the ratio remains almost the same.

Therefore, it is almost clear that we can replace SortedDictionary with LINQ and without it .stripped down version of .net in Silverlight is not a bad idea :-). Also, those who commented on my last post for live comparison thank you !!. You can also download the test code I have used here and run it in dottrace to see it live (I have used dottrace 3.1).

Have fun!!

kick it on DotNetKicks.com
Posted: Jun 18 2008, 03:09 AM by mehfuzh | with 2 comment(s) |
Filed under: , , ,
Replace SortedDictionary with LINQ query

With LINQ.Flickr it is quite necessary to get signature on parameters in order to do authenticated flickr photo get. As with the signature, it has to be sorted by parameter then to be hashed by MD5. Previously, I used to use SortedDictionary dictionary to do so, but thinking a little bit I learned that we actually don't need SortedDictionary anymore after we have LINQ. May be that's why the product team at Microsoft removed SortedDictionary from stripped down version of .net that comes with SilverLight.

Now, off to code, lets say I want 10 photos from my photo stream in flickr, to achieve that  I would simply write

var query = (from photo in context.Photos
            where photo.ViewMode == ViewMode.Owner
            select photo).Take(10);

Behind the scene, it will first try to authenticate me, if I am not already then it will try to do it and finally it will make an authenticated call to get my photos from my flickr account. Using SortedDictionary, the sorting of signature items used to be done on the fly but the overhead is that on every new item inserted in the dictionary due to each parameter of REST call, it runs the sort logic. This is of course a waste of processor speed. So, I replaced all the lines that look like

IDictionary<string, string> sortedSigItems 
= new SortedDictionary<string, string>();

with

IDictionary<string, string> sigItems = new Dictionary<string, string>();

Finally, inside my GetSignature method at BaseRepository of LINQ.Flickr I added the following lines, before final toString stuff.

var query = from sigItem in sigItems
            orderby sigItem .Key ascending
            select sigItem.Key + sigItem .Value;

// do the rest with sorted list.

The whole thing is much pleasing with LINQ query and less processor intensive.So next time when you think about sorting, think about LINQ first :-)

Have fun!!!

Continued with comparison in part 2 of this post.

kick it on DotNetKicks.com
Posted: Jun 17 2008, 01:43 AM by mehfuzh | with 4 comment(s) |
Filed under: , ,
More Posts