Thursday, July 30, 2009

NHibernate and ASP.NET - UpdateModel problem

Using NHibernate as a persistence layer is fun. Not only does it hide the complexity of a database completly but also allows one to use an object model instead of playing around with database query results.
In the case of NHibernate it's even better as it provides session management for web applications so with minimum effort you can just use lazy loading from within your views very easily.

At some point though problems will emerge. One of them is using ISession.Get without specifying the type (which might be unknown at the time of querying or might depend on the actual request from the page) with Controller.UpdateModel method. Here's how it goes:

1. UpdateModel is a generic method that expects the actual type that's being mapped to as the generic parameter.
2. It then passes the processing to TryUpdateModel which is a generic method as well.
3. TryUpdateModel takes this generic parameter, retrieves it's type metadata and uses it (among other things) to fill in a ModelBindingContext structure.
4. This ModelBindingContext is then passed on to IModelBinder.BindModel method.

It's all nice and dandy if we can specify the model's type. But if we don't know the actual type it results in processing of a model of type Object and none of the properties will get mapped. Because of the fact that there's no specialized model binder for type Object it gets passed on to the DefaultModelBinder implementation held by ModelBinders.Binders.DefaultBinder.

There's however an extremly nice and easy solution to this. We're going to use the decorator pattern to wrap up the current default model binder and provide it with the necessary information from NHibernate's ISessionFactory.

Here's an example implementation of such a model binder that can provide this additional implementation. I've called it NHibernateAwareModelBinder.


public class NHibernateAwareModelBinder : IModelBinder
{
private readonly ICollection mappedClasses;
private readonly IModelBinder defaultModelBinder;

public NHibernateAwareModelBinder(
ICollection mappedClasses,
IModelBinder defaultModelBinder)
{
this.mappedClasses = mappedClasses;
this.defaultModelBinder = defaultModelBinder;
}

public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
{
string modelType = bindingContext.Model
.GetType().FullName;
if (mappedClasses.Contains(modelType))
{
bindingContext.ModelType =
Type.GetType(modelType);
}
}
return defaultModelBinder.BindModel(
controllerContext,
bindingContext);
}
}

And here's how you'd use this class in your Application_Start event:

ModelBinders.Binders.DefaultBinder =
new NHibernateAwareModelBinder(
SessionFactory.GetAllClassMetadata().Keys,
ModelBinders.Binders.DefaultBinder);

As you can see we're using the current default model binder as the actual binder but we're decorating it's behavior with type resolution. Nice, isn't it?

Have fun!

Saturday, July 11, 2009

Localizing ASP.NET pages using resources

Localizing ASP.NET pages using resource files is an easy thing to do. It requires just a few steps to get everything up and running but all of those steps have to be completed because otherwise it will not work.

  • Set the UICulture and Culture attributes on a page to "Auto"
  • Create an ASP.NET folder named App_LocalResources
  • In App_LocalResources create a resource file using the following pattern:
page.ext.lang-variant.resx

for example:

Default.aspx, en-us -> Default.aspx.en-us.resx
Default.aspx, pl -> Default.aspx.pl.resx

  • On the element that needs localization set the attribute meta:resourcekey to something meaningful, for example:

<asp:Label ID="LblText" Text="Hello, world!" runat="server" />

becomes

<asp:Label ID="LblText" Text="Hello, world!" meta:resourcekey="LblText" runat="server" />

  • Add resource key for the property to modify using the following pattern:

resourcekey.property

for example:

LblText.Text

  • DO NOT FORGET TO REBUILD AND RE-RUN THE APPLICATION!


Here's a complete and working solution for you to play around

ASP.NET-Localization.zip

Have fun!