Dynamic URL in MVC – A CustomRouteHandler

Scenario: Your customers need to have their own name in your application URL.

eg. http://www.yourapp.com/GOODCUSTOMERNAME

Usually in MVC you have to define your route table, but to have a dynamic route table based on your customer names is not that easy. That is where the custom route handler comes in to play.

Step 1: Changes in Global.asax.cs

We pass all the calls through our CustomRouteHandler.

public static void RegisterRoutes(RouteCollection routes)
   var b = new {controller = "Home", action = "Index", id = UrlParameter.Optional, area = ""};
   routes.Add("Default", new Route("{controller}/{action}/{id}", new RouteValueDictionary(b),
   new CustomRouteHandler()));

Step 2: Writing the CustomRouteHandler

Note: MyService is one of my business layer service to resolve the URLs. And I will send the resolved value to “Home/Index” with a parameter name called “name”. Cool! I have only one value to resolve.

public class CustomRouteHandler : MvcRouteHandler
   protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
      string customerName= string.Empty;
      var controller = requestContext.RouteData.Values["controller"].ToString().Replace("-", "_");
      if (string.IsNullOrWhiteSpace(controller))
          controller = "Home";
          // The initial call sometimes does not carry the controller name
       if (!string.IsNullOrWhiteSpace(controller))
           customerName = controller;
           var s = MyService.ResolveUrl(customerName);
               customerName= string.Empty;
               controller = "Home";
               requestContext.RouteData.Values["name"] = customerName; //My Index method in HomeController is accepting a parameter called "name", that is the resolved customer name.
       requestContext.RouteData.Values["controller"] = controller; //Update the Controller Name
       var action = requestContext.RouteData.Values["action"].ToString().Replace("-", "_");
       if (string.IsNullOrWhiteSpace(action))
           action = "Index"; //The initial call sometimes does not carry the action name
       //Since I don't have much to resolve in action name, I just pass the value. But if you have multiple resolution to the action (methods to refer), I think you can figure out how to change this code.
       requestContext.RouteData.Values["action"] = action;
       //At this moment you should have a correct/resolved values to the route data values, otherwise you will get a error page.
       return base.GetHttpHandler(requestContext);

That is all, everything will be handled by the custom route handler. If you want more logics to resolve, just put them inside the handler.
Just keep in mind the actual controller classes must have a way to get the resolved values from the URL. Just like I have used the “name” parameter.
With this approach the URL entered in the browser would look more customer specific, and will not change, but the back end custom route handler resolve the URL for our app.

Pretty neat. eh!


NHibernate and Spring.net MVC

I recently started working on a project which I decided to use nHibernate and springframework for .Net with MVC. Though I had experience working with the springframework with ASP.NET, applying MVC had its issues.

Concerns at Deployment

Few things that you should keep in mind is that MVC projects works only under Integrated Pipeline mode. But the basic configuration for Springframework supports both modes Classic mode or Integreated. This conflict occurred as when I tried to deploy the project.
In order to solve that matter I had to assign the preCondition attribute in handlers. Such as,

   <validation validateIntegratedModeConfiguration="false"/>
   <modules runAllManagedModulesForAllRequests="true">
      <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web" />
      <add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate32" />
      <add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
      <remove name="PageHandlerFactory"/>
      <remove name="ContextMonitor"/>
      <remove name="WebServiceHandlerFactory"/>
      <add name="PageHandlerFactory" preCondition="integratedMode" verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web" />
      <add name="ContextMonitor" preCondition="integratedMode" verb="*" path="ContextMonitor.ashx" type="Spring.Web.Support.ContextMonitor, Spring.Web" />
      <add name="WebServiceHandlerFactory" preCondition="integratedMode" verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web" />

After adding the above code in the web.config, web site started working fine. I forgot to mention about how to configure pipeline in IIS.

  1. Open IIS: type “inetmgr” in the Run command screen
  2. Expand the server node
  3. Go to the application pool
  4. Select the application pool that is selected for your web site
  5. Double click on the pool
  6. Change the value in “Managed pipeline mode” dropdown to “Integrated”

A common error

Then I got this error,

No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

This is a very common error, but the main reason would be that, springframework can’t detect the hibernate session.

  • First check for the OpenSessionView module is existing in the web.config file. If it is not then add this in modules section
  • Secondly check whether the service layer calls are wrapped with a transaction attribute. It does not matter whether the call is read or write to the database.

I am telling this because I have a solution that have MVC Web Site and a windows application that uses the same data access library. All the configuration worked perfectly with the MVC after setting up as I have mentioned in the above of this post. But when it comes to the windows application it was throwing this error. Then I managed to get rid of it by specifying the “Transaction” attribute on the particular method that was called in the windows application.

I assume the reason behind this is windows applications are bound to maintain a dedicated session for the user, there’s no multiple session handling.

Transaction attribute

With the below configuration in your data access config file, transaction is enabled.

NOTE: WORDPRESS DOES NOT ALLOW ENTERING “Object” elements in anyway, so I have change that to “hobject”. when you use make sure to rename it back.

<hobject id="transactionManager"
       type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">
    <property name="DbProvider" ref="DbProvider"/>
    <property name="SessionFactory" ref="NHibernateSessionFactory"/>

I assume you have the DbProvider and NHibernateSessionFactory objects defined. If not do as follows, Following is  the DbProvider object, if you look at the database connection details they are passed as parameters using ${db.user} as an example. This way you get to change the db config details even after deployment, otherwise you will have to do a release, as the config data is in the compiled data-access-config.xml file. How this is done I have explain in this post.

<db:provider id="DbProviderprovider="MySql.Data.MySqlClient" connectionString="server=${db.datasource}; User Id=${db.user};database=${db.database};password=${db.password};Persist Security Info=True"/>

<hobject id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate32">
   <property name="DbProvider" ref="DbProvider"/>
   <property name="MappingAssemblies">
   <property name="HibernateProperties">
         <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
         <entry key="dialect" value="NHibernate.Dialect.MySQLDialect"/>
         <entry key="hibernate.connection.driver_class" value="NHibernate.Driver.MySqlDataDriver"/>
         <entry key="hibernate.current_session_context_class" value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate32"/>
         <entry key="use_proxy_validator" value="false"/>
         <entry key="format.sql" value="true" />
         <entry key="show_sql" value="true" />
   <property name="ExposeTransactionAwareSessionFactory" value="true" />

To support transaction in service layer add the following configuration in the service layer config.


Then add the [Transaction] attribute on the methods declaration where you want to enable transaction in the service layer. And make sure that you use CurrentSession.Flush() method at the end of those methods to commit all changes to the database.

Happy coding!!!

(I am tired now 2 posts at a stretch ;-))

NameValueSectionHandler a handy value parser between config files

I am going to explain a feature that will definitely come in handy in configuration files. Its about the NameValueSectionHandler.

Add the following section to the <configsections> in your web.config file.

<section name=”databaseSettings” type=”System.Configuration.NameValueSectionHandler” />

Now you get your own section with the name of databaseSettings.

   <add key="db.datasource" value="myservername" />
   <add key="db.user" value="root" />
   <add key="db.password" value="******" />
   <add key="db.database" value="Databasename" />

What happen here is making these key value pairs accessible in another config file. Same thing as AppSettings section except that AppSettings are been used in the application code itself, and this databaseSettings section I am going to use in another config file.

I am using springframework, so in my DataAccess layer I have a database-config.xml file, and that is the place where database configuration sections are available. If I had just placed the config values in the xml itself then it would be compiled at the time of deployment, and will not let me update later, unless I do a new release/compiled code.

With the help of above approach, I am able to do as follows,

<db:provider id="DbProvider" provider="MySql.Data.MySqlClient"
 connectionString="server=${db.datasource};User Id=${db.user}; database=${db.database}; password=${db.password}; Persist Security Info=True"/>

Now I have nothing to worry, the db configuration section is in the xml file and values are in the web.config file.

Syntax in accessing the variable is ${ PARAMNAME }


Happy coding!!!