ASP.NET MVC tips: Routing Engine to aid SEO / 301 Redirect / Tracking

Routing Library resides in the System.Web.Routing Namespace of the .NET Framework 3.5, which provides us the flexibility to use URLs that has no mapping to a physical file. This means ASP.NET MVC framework provides flexible URL mapping engine and enables us to write SEO (Search Engine Optimization) friendly URLs with very little effort. No one can deny the importance of SEO, to be successful in search based marketing. What better way to analyze a business than from what customers are looking for on the Internet through keyword research. SEO is the way to go.

SEO friendly URL Format for an e-commerce application may be
/Products/List/ProductCategory   
/Products/Detail/ProductName


URL Example
/Products/List/CareCare
/Products/Detail/MiracleCarDuster

In ASP.NET MVC world the URL Routing System maps the incoming URLs to the relevant Controller and Action, in the above example our Contoller is Products and Action is List or Detail. We normally go and define a Route object and add it to the RouteCollection and register the RouteCollection during Application_Start(). Out of the box System.Web.Mvc library ships with the RouteCollectionExtensions which allows us to define routes easily using the the different overloads of MapRoute method.

Example:


public static void RegisterRoutes(RouteCollection routes)
{
            routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);             


           routes.MapRoute(
                “Default”,                                              // Route name
                “{controller}/{action}/{id}”,                           // URL with parameters
                new { controller = “Home”, action = “Index”, id = “” }  // Parameter defaults
            );


}

If you look under the hood you will find, a Route object is created and added to the RouteCollection.

Route route = new Route(url, new MvcRouteHandler()){};
…..
…..
routes.Add(name, route);

note that “MvcRouteHandler” have been passed as a parameter by default, when we use the routes.MapRoute() method. The overloads of MapRoute() extension methods are just helper methods to make things easy for us, it is not mandatory that we will have to always use this. We can define a Route object in plain .NET code and assign necessary properties to it, we can also pass our preferred IRouteHandler. This gives superb flexibility with handling URLs. For instance In a practical world of web marketing / search marketing we always need to support Legacy URLs. In the web marketing world ad hoc campaigns are launched, and what may have worked last week may not work this week any more, as a result the URL structure changes frequently and we face the need to start redirecting to the new URLs.

On Top of that during this transformation of URLs we need to implement “301 Moved Permanently” redirections, which means the previous URL has been permanently removed and all future requests should be directed to the given new URI. Handling this scenario has become very easy with the Routing Engine. All we need to do is add a new Route or modify the existing Route to fit our need. Matt Hawley has an excellent post on Legacy Url Routing which describes how to route existing aspx file based URLs to the appropriate MVC Controller and Action. This article also gives directions on how to implement custom Route and custom RouteHandler.


public static void RegisterRoutes(RouteCollection routes)
{
            routes.IgnoreRoute(“{resource}.axd/{*pathInfo}”);             


           routes.MapRoute(
                “Default”,                                              // Route name
                “{controller}/{action}/{id}”,                           // URL with parameters
                new { controller = “Home”, action = “Index”, id = “” }  // Parameter defaults
            );


  routes.Add(“”, new LegacyRoute(
    “Users/Login.aspx”,
    “Login”,
    new LegacyRouteHandler())); // Defined a Custom Route class and have passed a custom IRouteHandler.


}





As you can see in the above code, how easily we can add a custom route and pass a custom IRouteHandler (in this case LegacyHandler).


Tracking – is another common task performed, we want to track everything, every single clicks a consumer performs on the site. This has also becomes easy in the world of ASP.NET MVC. By design we define separate actions for each functionality and we Route to the Controller – Action to get anything done. So tracking would be a viable option to do centrally just before creating the Controller object or just before delegating to the Action.

You will notice the implementation of IRouteHandler requires to implement only one method “GetHttpHandler” which returns a IHttpHandler


public interface IRouteHandler
{
        IHttpHandler GetHttpHandler(RequestContext requestContext);
}

You will find the MvcRouteHandler implements IRouteHandler like this


public class MvcRouteHandler : IRouteHandler {
        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
            return new MvcHandler(requestContext);
        }


}

The real delegation of a Route to a Controller and Action happens in the ProcessRequest(HttpContext context); method of the MVCHandler class which is the implementation of IHttpHandler. The ProcessRequest(HttpContext contxt) method may be a be a good place to centrally control tracking in one of our custom Handlers. The implementaiton of MVCHandler is as follows, where you can see how a controller is created by the IControllerFactory factory, and then Execute() method is called. We can do our tracking somewhere before calling the Execute() method.


protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
            AddVersionHeader(httpContext);


            // Get the controller type
            string controllerName = RequestContext.RouteData.GetRequiredString(“controller”);


            // Instantiate the controller and call Execute
            IControllerFactory factory = ControllerBuilder.GetControllerFactory();
            IController controller = factory.CreateController(RequestContext, controllerName);
            if (controller == null) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.ControllerBuilder_FactoryReturnedNull,
                        factory.GetType(),
                        controllerName));
            }
            try {
                controller.Execute(RequestContext);
            }
            finally {
                factory.ReleaseController(controller);
            }
        }



Did you know, Routes in the ASP.NET Mvc are matched and executed on a first match bases! It may be important to order the routes so that the correct pattern is matched first before a more general pattern matches and executes it. It is sometimes hard to figure out which particular pattern will be caught first when we have a lot of routes, ASP.NET Routing Debugger comes in to rescue, Phil Hack has put together this nice little route tester utility which can save a lot of time. This utility quickly displays in Red and Green color what Route patterns have matched for a particular URL. So we can type in various URLs in the addressbar to see which routes matches.

Lets now looks at a different problem, we normally define all the routes in the global.aspx.cs file, this causes a problem when Routes changes frequently, every single time a new route is added or an existing one is modified we need to recompile web application and upload the new dll to the server, again it is not mandatory to write Routing rules in the global.aspx.cs file, we can easily store the routing rules to a Xml file and use a combination XML related .NET libraries and .NET Reflection APIs to read from the Xml file and create/deserialize Route Objects to add them to the RouteCollection during the Application_Start(). But still we haven’t overcome the limitations of restarting the application as the RouteCollection gettting registered during Application_Start. I think we have to live with that, unless we go and implement some kind of  FileSystemWatcher to monitor the Xml file and force to refresh the RouteTable.Routes object when the xml file changes. I haven’t tried implementing this yet but this would work I think.

We have discussed here, how ASP.NET Routing engine eases writing SEO friendly Url, maitaining Url redirections and tracking centrally. Hope this helps.

Thank you for being with me so far.


Shahed Khan
CEO Simplexhub
Co-Founder PageFlakes
Founder Smart Code Generator


 

ASP.NET tips: Display resultset from Multiple DataTable

I normally do not use DataSet and prefer Objects instead generated by the ORM frameworks, but recently I had to produce a ASP.NET page that displayed a list of records from multiple DataTables. Lets look at a similar example.

Lets assume we consume this DataSet which has two DataTables “Agent” and “RealEstateProperty”. The task is to display, what properties belongs to which agent, in a GridView.

RealEstateDataset 

There are different ways to accomplish this task, lets look at them one by one.

Method 1: Bind DataTables to  a single GridView

To bind these multiple DataTables to a single GridView control, we can quickly create a new temporary DataTable, with the required fields and populate the Rows and then bind the new DataTable Rows to the GridView.


BindSingleGrid


The above code is self explanatory, where I have created a temporary DataTable, populated its rows by iterating through the original DataTables and then binded the Grid to the new DataTable.Rows. The interesting piece of code to note here is the row.GetChildRows(), which respects the relationship and automatically returns the related child rows. The aspx part looks like the following, where we have a GridView with three columns.

SingleGridView)


and the RowDataBound code is

 SingleGridRowBound


Method 2: Bind DataTables to a nested GridView


If we want to avoid creating temporary dummy DataTable as described in method 1, we can use the technique of nested list control. Now we can do this by using

two GridView, or
two DataList, or
two Repeaters or
a combination of Repeater and GridView, or
a combination of Repeater and DataList, or
a combination of GridView and DataList.

We are going to look at a combination of 2 GridViews here, where one GridView is nested inside another, you can do any of the above combinations. For this case the the aspx code is,nestedgridview


Notice the parent and nested GridViews has different onrowdatabound methods. In this technique the parent grid is binded to the the Agent DataTable Rows and the nested GridView is binded to the child RealEstateProperty DataTable Rows. Here is the code.
nesteddatagridbind


Method 3: Convert the DataSet to Objects and then bind to GridView

We can generate csharp class from the dataset schema using Xsd.exe and then bind the GridViews to the objects. I have discussed similar technique in one of my previous blog post, where you will find how we can use the Xsd.exe that ships with the .NET Framework.

Example
    C:\temp>xsd propertyDataSet.xsd /l:cs /c
    Microsoft (R) Xml Schemas/DataTypes support utility
    [Microsoft (R) .NET Framework, Version 2.0.50727.42]
    Copyright (C) Microsoft Corporation. All rights reserved.
    Writing file ‘C:\temp\propertydatasetclass.cs’.


I also demonstrated a handy DataTableToT() method to assist in converting DataTable to strongly typed object. And when we have the DataTable converted to a stronglytyped object/list/collection it is very easy to bind to the bind to the GridView.


 


Other Tips
1. DataSet also comes with handy Merge Method, check here, we could have also used this method.
2. When we load a DataSet from xml schema if we do not provide the strongly typed DataSet it will not preserve the Relationship automatically.


 onlydatasetQuickWatch


Note: The Relations Count = 0;

3. To preserve Relations automatically during a DataSet load from xml, provide strongly typed DataSet. Both of the following methods will automatically preserve the parent child relationship. Also even if you consume them from a webservice client application they will preserve the Relations.


stronglytypeddataset


4. DataSet.WriteXml() method serializes the DataSet content and writes XML data.
5. DataSet.WriteXmlSchema() method writes the DataSet structure as XML Schema (xsd file).


 


Conclusion
We have discussed different techniques to bind multiple DataTables to GridView, we have also discussed some handy tips about the DataSet. Hope this helps.


Shahed Khan
CEO Simplexhub
Co-Founder PageFlakes
Founder Smart Code Generator