Monday, 7 November 2011

MVC Lower-case URLs

SEO guidelines usually recommend that a site's URLs should be kept in all lower-case. The reason for this is that search engines and web servers alike (IIS is a notable exception) will treat two differently cased URLs as two different resources. While the host name of a URL is case insensitive (i.e. there's no difference between http://www.mysite.com and http://www.MySite.com) the resource path is not. Therefore http://www.mysite.com/home and http://www.mysite.com/Home are considered different resources.

While this might not make much sense semantically, consider this: The world's most widely used web server, Apache, treats URLs as case sensitive. Therefore the above URLs do, in fact, represent two different pages. As such, search engines treat these two URLs as different pages, too - and if your website doesn't care about URL casing you might end up with a split index for your pages.

So - how do we ensure that your website generates only lower-case URLs? With ASP.NET MVC this is easy. All you need is:
  • A LowercaseRoute class
  • An extension method for RouteCollection
  • An extension method for AreaRegistrationContext


And yes - the solution I'm about to detail will work with MVC Areas.


The LowercaseRoute class extends the Route class and basically lets that class do all the work. LowercaseRoute just ensures that the host and path portions of he URL are turned to lower-case while the querystring portion is left alone:

internal class LowercaseRoute : Route
{
public LowercaseRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler){}

public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler){}

public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler){}

public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler){}

public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var path = base.GetVirtualPath(requestContext, values);

if (path != null)
{
var virtualPath = path.VirtualPath;

if (virtualPath.LastIndexOf("?") > 0)
{
var leftPart = virtualPath.Substring(0, virtualPath.LastIndexOf("?")).ToLowerInvariant();
var queryPart = virtualPath.Substring(virtualPath.LastIndexOf("?"));
path.VirtualPath = leftPart + queryPart;
}
else
path.VirtualPath = path.VirtualPath.ToLowerInvariant();
}

return path;
}
}


The RouteCollection extension method creates an instance of LowercaseRoute and adds it to the route collection:

public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults)
{
if (routes == null)
throw new ArgumentNullException("routes");

if (url == null)
throw new ArgumentNullException("url");

var route = new LowercaseRoute(url, new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(defaults)
};

routes.Add(name, route);
return route;
}


The AreaRegistrationContext extension method calls the MapRouteLowercase extension method on RouteCollection and also adds the current context's AreaName property to the route's DataTokens collection. This second step is crucial for areas to work:

public static Route MapRouteLowercase(this AreaRegistrationContext context, string name, string url, object defaults)
{
var route = context.Routes.MapRouteLowercase(name, url, defaults, constraints, namespaces);

route.DataTokens["area"] = context.AreaName;
return route;
}


And that's all you really need. Of course, you may want to add more extension methods of your own so that you can add route constraints or any other data that your route handler may need (such as namespace differentiators). But I'll leave that for you to flesh out on your own. Happy coding!

Tuesday, 1 November 2011

Route Constraints: Working with conflicting routes in MVC

Routing is one of the things in the MVC framework that seems to just 'get me' every once in a while. Routing isn't that hard - it's pretty straight forward stuff. Routing is something you should, ideally, set up once - and then forget about. In order to do this, though, you need to have a plan up front - and you don't always have that (personally I think it's best if you've got a site map and a URL schema worked out before you start). If you add routes as you need them you can end up with more routes than you need and route conflicts that you didn't foresee.

This isn't intended as a post about MVC routing in general. Rather I wanted to post about a little piece of work I had to do in order to make use of two routes that conflict with one another. I think these routes highlight the most common problem people have when they're getting to grips with MVC routes; your URL is matched by the wrong route entry.

I like to keep things simple, and I don't like making extra work for myself. As such I like the default route that any Visual Studio MVC project comes set up with:
routes.MapRoute(string.Empty, "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });

This route entry is simple to understand and you can build large applications based entirely on this single route entry. Of course, you may want to add some "pretty" routes as well such as "/login" or "/signup" - but that single route is really all you need. That is, until you decide that not all your URLs should be on the form "{controller}/{action}/{id}."

In my case, I also needed a route like this:
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Clubs", action = "Index", id = UrlParameter.Optional });

This second route is important to my application because it's used to display certain entities by their unique identifier. Such entities can be a member of the site, a club, or a store. Example URLs are:

http://mywebsite.com/members/oyvind
http://mywebsite.com/stores/oyvindsmegamart
http://mywebsite.com/clubs/oyvindsgreatbigfanclub

As you can tell from the example URLs, several controllers can be mapped by this route and, if they are, the route should always map to the "index" action method and pass in the "id" parameter. But, if you add these routes to your application, you'll run into trouble because they conflict with each other. Let's look at why.

An incoming URL will be matched by one route and one route only. Routes are examined one by one and the first match is the one that will be used. This means that the order in which we add routes is important. We always want our most specific routes to be listed first. Let's apply this principle to our two routes:
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Clubs", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(string.Empty, "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });

Here I've added the "{controller}/{id}" route first because I feel this is the more specific of the two routes. Now let's look at a sample URL:

http://mywebsite.com/members/oyvind

If we break this URL down you'll see that the "/members" section of the URL will map to the "{controller}" portion of both routes. Also, the "/oyvind" section of the URL will map to the "{id}" portion of the first route. Happy days! We have a match! It looks like our two route entries might work after all.

But not so fast. What about this URL?

http://mywebsite.com/account/changepassword

This URL should look familiar to you as it's more of a traditional MVC route; in fact, it's a classic default route for any standard MVC project. But will this work with our two route entries? The "/account" section of the URL will map to the "{controller}" portion of both routes. The "/changepassword" section of the URL will map to both the "{id}" portion of the first route, and the "{action}" portion of the second route. However, because the first match wins, the first route is chosen and the request will end up being directed to the Index action method on the AccountController class, with an id parameter of "changepassword"... this isn't what we intended.

What happens if we change the order of the routes?
routes.MapRoute(string.Empty, "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Clubs", action = "Index", id = UrlParameter.Optional });

Let's look at the last URL from the previous example first:

http://mywebsite.com/account/changepassword

The "/account" section of the URL will map to the "{controller}" portion of both routes. The "/changepassword" section of the URL will map to both the "{action}" portion of the first route, and the "{id}" portion of the second route. In this case our first route will be selected - and the request will be directed to the ChangePassword action method on the AccountController (with an empty id). This is the desired result for this URL. But what about this URL?

http://mywebsite.com/members/oyvind

The "/members" section of the URL will map to the "{controller}" portion of both routes. The "/oyvind" sectin of the URL will map to the "{action}" portion of the first route, and the "{id}" portion of the second route. Because of the order of precedence, the first route will be selected and our request will be directed to the Oyvind action on the MembersController, with an empty id. Most likely we'll end up with a "404 - Not Found" because I doubt very much you'll have an action called Oyvind on any of your controllers.

I need both routes to work, but they clearly conflict with each other and changing the order of the routes doesn't actually help. What can I do? Somehow I need to help the MVC framework understand when to pick one route over the other. Thankfully there's a built-in mechanism we can leverage to help us: the route constraint.

When you add a route to the route table you can specify that this route has certain constraints. A constraint applies to a portion of the route (for example the "{id}" portion) and can set out that this portion has to match certain values, be of a certain format, or exclude specific values. When defining routes you pass the constraints as a third parameter to the MapRoute method:
routes.MapRoute(string.Empty, "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { action = "MySpecialAction"});

In this contrived example I've specified a route using the standard/default route pattern, but I've specified a constraint for the "{action}" portion of the route. The constraint states that unless "{action}" equals "MySpecialAction" the route will not be matched. This route constraint is actually a regular expression constraint, so you if you want to allow "{action}" to include not only "MySpecialAction" but also "YourSpecialAction" you can alter the route entry as follows:
routes.MapRoute(string.Empty, "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { action = "MySpecialAction|YourSpecialAction"});

You can read more about route constraints here (http://www.asp.net/mvc/tutorials/creating-a-route-constraint-cs) as I'm not going to dwell on the specifics here. Rather, I want to get on with the problem at hand. Let's break it down a little:
  • I want to use the default route "{controller}/{action}/{id}" as much as possible. This is the route I want to base my whole site on.
  • In some special cases I want the route "{controller}/{id}" to take precedence. At the moment I know that I want this route to take precedence for the MembersController, the ShopsController, and the ClubsController.
  • I want to be able to define action methods other than Index on the MembersController, ShopsController, and ClubsController - and I want these actions to be matched by the default route.
The problem we encountered with the routes in their raw, unconstrained, form is that the "{id}" portion of a route will happily match the "{action}" portion of the other route, and vice versa. Since we add the most specific route first
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Clubs", action = "Index", id = UrlParameter.Optional });

we need to make sure that the "{id}" parameter does not match any action methods on the controller. Also, we don't want this route to apply to all controllers, so we need to constrain the "{controller}" portion of the route to the desired controllers. Let's start with the controller constraint first. We want this route to only apply to the ClubsController, ShopsController, and MembersController:
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Clubs", action = "Index", id = UrlParameter.Optional },
new { controller = "Clubs|Members|Shops"});

That's all we need to do.

The other constraint, however, is a little bit more involved. The route should not be matched if the "{id}" portion of the URL matches any of the action methods on any of the controllers that this route applies to. We can do this by applying another regex constraint which contains the names of all the action methods on these controllers:
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Clubs", action = "Index", id = UrlParameter.Optional },
new
{
controller = "Clubs|Members|Shops",
id = "ClubMembers|Photos|News|Deals|Staff|Wall|About|OpeningHours|Info|Friends"
});

The above example is contrived - but it attempts to highlight a problem with the approach. Not only will this list of action names grow very quickly, you will also have to remember to add action names to this list whenever you add an action on any of the controllers (or modify the list if you change the name of any of the action methods). This approach will work, but it's not a very maintainable solution.

A better approach would be to use a custom route constraint. A custom route constraint is a class which implements the IRouteConstraint interface. This interface defines a method called Match:
bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);

The job of the custom route constraint is to decide if a given route parameter is valid for the given route. We want to check a single parameter, "id" against a potentially large list of values. To this end, I've created a ValuesConstraint class:
public class ValuesConstraint : IRouteConstraint
{
private readonly bool _include;
private readonly string[] _values;

public ValuesConstraint(params string[] values) : this(true, values){}
public ValuesConstraint(bool include, params string[] values)
{
_include = include;
_values = values;
}

public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return _include && (_values.Contains(values[parameterName].ToString(), StringComparer.InvariantCultureIgnoreCase));
}
}

The ValuesConstraint class is instantiated by passing in the list of values, and an an optional flag which indicates if the route parameter should be a match or not be a match for these values. It can be used in the following fashion:
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new
{
controller = "Clubs|Members|Shops",
id = new ValuesConstraint(false, "ClubMembers", "Photos", "News", "Deals", "Staff", "Wall", "About", "OpeningHours", "Info", "Friends")
});

While the above would work it doesn't actually solve the problem of maintainability, because we're still hard-codign a list of strings representing the action methods on our controllers. So, the final piece of the puzzle is to create a method that outputs a list of all the action methods on controllers of our choosing:
private string[] GetActionNames(params Type[] controllers)
{
var actionNames = new List();
foreach(Type controllerType in controllers)
{
MethodInfo[] methodInfos = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
foreach(var method in methodInfos)
actionNames.Add(method.Name);
}

return actionNames.ToArray();
}

And then we can use it like this:
var controllerActions = GetActionNames(typeof(ClubsController), typeof(MembersController), typeof(ShopsController));
routes.MapRoute(string.Empty, "{controller}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new
{
controller = "Clubs|Members|Shops",
id = new ValuesConstraint(false, controllerActions)
});

Now our "{controller}/{id}" will only be matched by URLs where "{controller}" equals "Clubs", "Members", or "Shops" and "{id}" does not equal any action method name on any of these controllers. Any URL that does not match this route will then default to our, uhm, default route. Ta-dah!

Thursday, 13 October 2011

Using DomainTime Instead of DateTime

In a previous post about favouring DateTime.UtcNow over DateTime.Now I mentioned that I don't use the DateTime struct directly in code to obtain the current date or time. Instead I use what I call DomainTime, a wrapper around the DateTime struct. It looks like this:
   public static class DomainTime
{
internal static DateTime OverrideForTesting = DateTime.MinValue;

public static DateTime UtcNow { get { return OverrideForTesting == DateTime.MinValue ? DateTime.UtcNow : OverrideForTesting.ToUniversalTime(); } }

public static DateTime Now { get { return OverrideForTesting == DateTime.MinValue ? DateTime.Now : OverrideForTesting; } }

public static DateTime Today { get { return OverrideForTesting == DateTime.MinValue ? DateTime.Today : OverrideForTesting.Date; } }

public static DateTime MaxValue { get { return DateTime.MaxValue; } }

public static DateTime MinValue { get { return DateTime.MinValue; } }

internal static void Reset()
{
OverrideForTesting = DateTime.MinValue;
}
}

The whole point of using this class is that it makes it easy to test other classes that have some kind of time dependency. To use a contrived example, imagine that you've got a class which will only do its work if the time is between 1am and 2am. It makes the decision (should I work or should I not?) by checking DateTime.Now.

How do you test this? You _could_ run your tests just before 1am, between 1am and 2am, and then again after 2am - but that's just stupid. You should be able to run your tests anywhere, any time, and as many times as you want.

The solution, here, is to make the class depend on DomainTime.Now instead. By doing so, you can
override the current time during testing by setting it with the OverrideForTesting property.

Note that this property is an internal property. Expose it to your test assemblies by using the
[assembly: InternalsVisibleTo("Your.Test.Assembly")]

directive in the AssemblyInfo class in the assembly where DomainTime resides.

Now you can test to your heart's content. It's just a matter of setting the appropriate DateTime for each test.

One final note: The DomainTime.Reset() method is there for your SetUp() or TearDown() methods so that you can avoid having the DateTime set by one test bleed over into another test.

Binding Views to Navigation Elements

Last night I did a bit of work on how I bind views to navigation items. I have tended to include information about 'active tabs' as part of the view model (which fits well with the idea of having one model per view) - but I didn't like the hierarchy of view models that emerged from it.

UPDATE: After a an anonymous comment on this post I have updated the implementation to use ViewData rather than TempData as the commenter rightly pointed out that, while the TempData implementation will work, TempData is for redirect.

What I ended up doing was sticking a piece of data in the ViewData dictionary and pulling it out in the view to determine which tab should be rendered as 'active'. I created some extension methods for ViewData to do this:

 
public static class ViewDataExtensions
{
public static void SetNavigation(this ViewDataDictionary viewData, T navElement)
{
var key = GetNavKey(navElement);
viewData.Add(key, navElement);
}

public static T GetNavElement(this ViewDataDictionary viewData)
{
var key = GetNavKey(typeof(T));
T t;
try
{
t = (T)viewData[key];
}
catch
{
t = default(T);
}

return t;
}

private static string GetNavKey(T navElement)
{
return GetNavKey(navElement.GetType());
}

private static string GetNavKey(Type t)
{
return "sitenav:" + t.Name;
}
}

Then I created an action filter which I can stick on a controller and/or an action. Notice the AttributeUsage which specifies the allowable targets and that the attribute can be applied more than once (this could be important if you've got more than one menu):

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple = true)]
public class BindNavigation : ActionFilterAttribute
{
private readonly object _navElement;

public BindNavigation(object navElement)
{
_navElement = navElement;
}

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData.SetNavigation(_navElement);
base.OnActionExecuting(filterContext);
}
}

I apply this to a controller like this:
   
[BindNavigation(MainNavigation.Members)]
public class MembersController : FlyingFieldsBaseController
{
...
}

And an action like this:
 
[BindNavigation(ProfileNavigation.Wall)]
public ActionResult Index(string id)
{
var model = _getMemberProfileViewQuery.Invoke(Guid.Parse(id));
return View(model);
}

As you can tell I am using enumerations for my different "types" of navigation (main header nav, left
hand nav for profile pages, left hand nav for club management pages, etc). The thing is, though, you
can use anything you like - because TempData stores things as objects (you'll notice that my TempData extension methods use generics so you get type safety as well). You could, for example, store an object that holds the state for several layers of navigation if that's what you need.

In the views you just do this:
@{
var currentSection = ViewData.GetNavElement();
}

and use 'currentSection' however you please. I just use it to determine if I should set a CSS "selected" class on my navigation items.

Thoughts are welcome!

Wednesday, 5 October 2011

Working with dates

Lately I've been doing a good amount of work on a website feature that requires the output of a relative date and time. For example, a entry written on a user's wall (the website has social aspects) may be annoted with "written by John Smith about two hours ago." It is a relatively straight forward task to accomplish this - but I thought I'd write a few words about some pitfalls you may come across.

Even if you have only every written the most trivial of applications you are likely to have used the DateTime structure in .NET (if you've written .NET apps, that is). And you're very likely to have used DateTime.Now; Let's take a closer look at the DateTime structure. According to Microsoft's documentation a DateTime "Represents an instant in time, typically expressed as a date and time of day." And, DateTime.Now "Gets a DateTime object that is set to the current date and time on this computer, expressed as the local time."

Cool. DateTime.Now is really handy for getting a handle on the current time, and we've all used it. But now I'm going to tell you that you shouldn't.

DateTime.Now returns a DateTime object that represents "the current date and time on this computer, expressed as the local time." As convenient as this may be, it's a potential source of trouble. "This computer" is the computer where the code executes. It could be your desktop machine, a development server, a production server, or a mobile device. The time on that machine depends on where the machine is located (which timezone it is in) and whether or not the machine is affected by daylight savings. So why is this a problem?

The problem is that if you cannot guarantee that all the machines in your infrastructure are all in the same timezone and are equally affected (or not affected) by daylight savings, using DateTime.Now in your code will potentially yield different timestamps on different machines even if the call to DateTime.Now was made at exactly the same time on the machines in question. 1pm in Oslo, Norway, on October 5th 2011 is not the same time as 1pm in London, UK, on the same date. "But that's just silly", I hear you say. "All our infrastructure is in the same data centre in one place." OK. Fine. That may very well be the case. But what about your users. Where are they? Are all of them in the same time zone as your servers? And is it likely that you'll never grow beyond having only local users and having only one data centre?

Even if your answer to the above questions is "we'll never scale beyond one data centre and all our users are in the same place and always will be" I think you should keep reading. It might just make your life a little simpler down the road. Just in case.

The problem with DateTime.Now is that it always represents the local time of the machine on which the code executes, and you don't really want to worry about where that machine is, because doing so makes life as a developer painful. What you want to do instead is use DateTime.UtcNow which returns an instance of DateTime representing the Coordinated Universal Time (UTC) of now. UTC is the local time of the server less any timezone differences and less any daylight savings difference. If you only ever store and use UTC DateTimes then none of your DateTime comparisons will ever have to take into consideration any time differences caused by time zones or daylight savings.

The only thing you now have to worry about is the thing you should worry about, which is displaying the correct date to your end users. You'll have to adjust for the timezone they're in because a date for an event at 12pm in London should be rendered as 1pm for a user in Oslo (they're always an hour ahead of London time).

Regardless of whether you use UTC or not, you'll always have to consider time zones and daylight savings when rendering dates for a user. Using UTC, that's all you'll have to worry about. If you use DateTime.Now, however, you'll also have to ensure that you know what the time offset of that DateTime instance is if you're going to compare it to another date, or if you're going to render it to a user. Pain in the arse (PITA).

I reckon that you should always use UTC times in your applications regardless of what your user base might look like. It makes life simpler from the start, and if you ever need to support users across different timezones you'll be a step ahead.

So, as a rule, this is what I do:

  • Always call DateTime.UtcNow and never DateTime.Now. (In fact, I don't use DateTime... I use DomainTime which is a wrapper I've created around DateTime. I'll write more about that in a later post).
  • Always treat DateTime stored in a database as UTC. This means that when I read that DateTime out of the database I specify that it is a UTC DateTime using the DateTime.SpecifyKind() method. This is very important, because the machine will by default treat any DateTime as local time.

    Note that if you're using an ORM such as NHibernate you need to tell the ORM that the date should be treated as UTC. With FluentNHibernate this is really simple:

    Map(x => x.DateCreated).CustomType().Not.Nullable();

  • Lastly, before displaying the date to the user, I apply the time difference between UTC and the user's location. There are several ways of doing this. For example you can have your users tell you which time zone they're in and you can apply the offset. Or, if your users are web users you can use Javascript's Date.getTimezoneOffset() method and apply the difference (in minutes) to your UTC date. Check out this StackOverflow question for some specific pitfalls of that particular method.

I'll follow up on this post with another post or two about displaying relative times and how to use a DomainTime wrapper around your dates.

Tuesday, 20 September 2011

NHibernate session management and MVC

NHibernate session management is one of these things that occasionally catches me out. Usually once it's set up on a project I forget about it because it just works, but recently I came across a problem on a project which prompted me to write this post. There's a lot to say about NHibernate session management and this post is just a small contribution to the plethora of articles on the subject.

What follows here is my implementation of session management in an MVC web application (it shouldn't matter what version of MVC you're on). What I've set out to accomplish is this:
  • Define a unit of work by using NHibernate's ICurrentSessionContext
  • Enforce the unit of work by applying transaction management across controller calls.
  • Ensure that a session and transaction is available across multiple calls to child actions.
  • Make the session available for injection via my container of choice, Windsor.
In order to make any of this happen my application needs a session factory. I'm not going to go into the details of how I construct my session factory as it isn't really relevant to this post - but suffice to say that the creation of the session factory is triggered by the Application_Start() method in Global.asax - and that I store the session factory as a static property on the HttpApplication. Actually, there is one aspect of the session factory which is relevant to this post - but I'll come back to that a little later.

The Unit of Work
In web applications the obvious scope for a unit of work is a web request. Everything that happens, persistence-wise, between when a new request arrives and when the same request completes, can be treated as a unit of work. In other words, the changes to state in your application that takes place within a single web request should be atomic; the should either be persisted or rolled back at the end of the web request.

With this in mind I've set up the following methods on Global.asax:

private void OnBeginRequest(object sender, EventArgs args)
{
if(RequestMayRequirePersistence(sender as HttpApplication) && Factory != null)
{
var session = Factory.OpenSession();
session.FlushMode = FlushMode.Auto;
ManagedWebSessionContext.Bind(HttpContext.Current, session);
}
}

private void OnEndRequest(object sender, EventArgs args)
{
if (RequestMayRequirePersistence(sender as HttpApplication) && Factory != null)
{
ManagedWebSessionContext.Unbind(HttpContext.Current, Factory);
}
}
Ignore the call to RequestMayRequirePersistence - this is just a helper method to check whether the request is for a resource that may require an NHibernate session, such as a controller.

When a request arrives, I call OpenSession() on the session factory to obtain a new session. Then I bind this session to the current HttpContext by calling the Bind method on NHibernate's ManagedWebSessionContext.

Let's take a step back. Remember that I said that there's one aspect of the session factory set-up that's relevant? Well, we've already come to it. In order for this solution to work we need to tell the session factory which type of session context to use. A session context defines the scope of given session instance. In other words, if a session instance belongs to a context then the NHibernate session factory will keep serving up that same instance whenever a call is made to OpenSession() within that context. We can tell NHibernate which session context to use when setting up the session factory. Using FluentNHibernate syntax you can set up NHibernate to use the ManagedWebSessionContext by adding this to your session factory fluent config:
   .ExposeConfiguration(config =>
config.SetProperty(Environment.CurrentSessionContextClass,
typeof(ManagedWebSessionContext).AssemblyQualifiedName))
Now let's go back to handlers for BeginRequest and EndRequest. In addition to telling NHibernate which session context we want to use, we have to define when to bind and unbind the current session from that context. That's what the OnBeginRequest and OnEndRequest methods do; they define the actual scope of our NHibernate session, our unit of work.

You can read more about NHibernate session context here.

Enforcing our Unit of Work
Now we've defined a unit of work for our application. However, this unit of work is no good to us yet because we still need to set up some controls around when transactions are created, committed, and rolled back. For my application I think the following rules make sense:
  • Begin a transaction when an action method starts executing
  • If an exception occurs during the execution of an action, roll back the transaction.
  • If an exception occurs during the rendering of a view, roll back the transaction
  • If the execution of a controller and rendering of a view complete successfully, commit the transaction
  • Any calls to child actions should 'enlist' in the current transaction, and a child action should never roll back or commit a transaction.
I have implemented these rules through an ActionFilterAttribute which I've called, uhm, TransactionAttribute. It looks like this:
public class TransactionAttribute : ActionFilterAttribute
{
//Create transaction
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as IPersistenceController;

if (controller != null && !filterContext.IsChildAction)
{
controller.CurrentSession.BeginTransaction();
}
}

//rollback on exception
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var controller = filterContext.Controller as IPersistenceController;

if (controller != null && !filterContext.IsChildAction)
{
ITransaction transaction = controller.CurrentSession.Transaction;
if (transaction.IsActive)
{
if (filterContext.Exception != null)
{
transaction.Rollback();
}
}
}
}

//commit transaction, or rollback on exception
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var controller = filterContext.Controller as IPersistenceController;
if (controller != null && !filterContext.IsChildAction)
{
ITransaction transaction = controller.CurrentSession.Transaction;
base.OnResultExecuted(filterContext);
try
{
if (transaction.IsActive)
{
if (filterContext.Exception != null)
transaction.Rollback();
else
transaction.Commit();
}
}
finally
{
transaction.Dispose();
}
}
base.OnResultExecuted(filterContext);
}
}
There are really only two things worth noting here:
  • Controllers are cast to IPersistenceController. IPersistenceController defines a single property ISession CurrentSession {get;set;} which is used to obtain the current session.
  • I check the IsChildAction property of the ActionExecutingContext to make sure that child actions don't mess with the current transaction.
You could argue that having every controller implement IPersistenceController is a bad thing and that letting the controllers know about ISession is even worse - and I'd probably tend to agree with you. If you don't like this approach you can of course abstract the access to the current session away from the controller. I've opted for this approach because of ease of implementation; Windsor - which I'll talk about in a minute - resolves all controllers for me, and therefore sets the ISession on the IPersistenceController.CurrentSession property automatically.

Making the Session Available to Windsor
The last piece of the puzzle is to make ISession available to Windsor so that it can be injected:
   container.Register(Component.For()
.UsingFactoryMethod(() => MvcApplication.Factory.GetCurrentSession())
.LifeStyle.PerWebRequest);
Note the use of a factory method, and the call to MvcApplication.Factory.GetCurrentSession(). When Windsor needs to resolve an instance of ISession it will call GetCurrentSession() on the application's session factory. The current session is governed by the session context which we set up earlier. See? So Windsor will be handed the same instance of ISession every time it asks the factory for it during the course of a single web request.

The last thing to notice is the lifestyle that's chosen for the ISession instance: PerWebRequest. If you choose anything other than this lifestyle you'll run into trouble:
  • If you set the lifestyle to Transient then Windsor will dispose of ISession if it disposes of any objects that have been injected with ISession
  • If you set the lifestyle to Singleton then Windsor will break the scope of the Unit of Work we've so carefully set up and you'll end up with a single session for the lifetime of the application.
Summary
In this post I've shown one way of managing NHibernate sessions in the context of an ASP.NET MVC application. There are many ways of doing this, but this way seems to work well for me. Comments are welcome!







Monday, 14 February 2011

Reflector is becoming a paid-for tool

Sadly, RedGate is breaking their promise (or, as they call it, 'intention') of keeping Reflector free of charge. So hurry, get your copy of Reflector now, before you have to pay for it.


Monday, 7 February 2011

Hadlow: IDependencyResolver Is Broken

In my haste and determination to use MVC 3.0's dependency resolver in order to enable injection on attributes I took some suggestions from Christian Prieto's blog post and implemented a Windsor-backed IDependencyResolver. It worked well and, despite the fact that I was effectively embracing an anti-pattern and using a new global variable, I was pretty happy with the solution.

I'd overlooked something really fundamental, however. Mike Hadlow points this out in his recent post on Code Rant. The issue is simply this: IDependencyResolver doesn't support a mechanism for releasing components, and this poses a problem when using a container such as Windsor as backing for the resolver. If you can't release components you'll eventually end up leaking memory.

In hindsight I should have stopped and listened more carefully to that gut feeling that told me I was doing something dirty when I embraced the service locator.

And for the first time I feel a bit let down by the MVC team; why did they ignore this (see the comments trail)? I have to agree with Hadlow on this: They shouldn't shout about the IDependencyResolver being container-agnostic when it blatantly isn't.

So this afternoon I'll be rolling my good old WindsorControllerFactory back into the solution.



Tuesday, 18 January 2011

Annoying issue with the ASP.NET membership databases

In order to speed up making changes to my databases during development I've set up a .cmd script that drops and recreates my project databases, and fills them with some seed data. This is pretty expedient, though in hindsight I think a solution using something like Visual Studio's database projects might be better. At any rate, I digress...

Yesterday I ran into a problem with the ASP.NET membership database tables. I kept getting this error:

The 'System.Web.Security.SqlMembershipProvider' requires a database schema compatible with schema version '1'. However, the current database schema is not compatible with this version. You may need to either install a compatible schema with aspnet_regsql.exe (available in the framework installation directory), or upgrade the provider to a newer version.

My script uses the aspnet_regsql.exe tool to install the membership database - so this was very strange. The actual error stems from data missing in the aspnet_SchemaVersions table. Depending on what membership features you have installed you will have some of the following rows in this table:

common 1 1
health monitoring 1 1
membership 1 1
personalization 1 1
profile 1 1
role manager 1 1

The weird thing for me, though, was that this data was present! So, what was going on?

The error was only thrown by IIS, but not by the development web server in VS. Discovering this prompted me to restart IIS... and the problem went away.

In my case I figured out that the problem was that my database script had deleted all the rows in the aspnet_SchemaVersions after the database had been created. This caused the error in the first place. When I updated the script, however, the error didn't go away (in IIS) and I can only guess that this must be because this data had been cached in the IIS worker process.