Localizing NHibernate: Contextual Parameters

I can't think of a good name for this post, but it is a very cool discovery. First, let us examine the scenario, a customer of mine has bought a database of localized data, which had the following structure:

(Image from clipboard).png

Now, what the customer wanted to be able to do is something like this:

Product product = session.Get<Product>(5);
productName.Text = product.Name;

And get the correct name for the user's culture. In addition to that, they didn't want to have to load the all the product names collection and get the name in memory. The database include several hundred thousnads of products, localized to several languagues, so this is a big perf concern.

I thought about this quite a bit, and was nearly at the point where I told them that it can't be done when I recalled that NHibernate 1.2 added filters capabilities. A quick testing proved that it is possible to do this with NHibernate, using the following approach:

First, we need to map the product, like this:

<class name='Product'>

       <id name='Id'

              column='id'>

              <generator class='native'/>

       </id>

       <property name='Amount'/>

       <property name='Size'/>

       <property name='Name'

                       formula='(SELECT ProductNames.Name FROM ProductNames

                       WHERE ProductNames.Id = id and ProductNames.CultureId = :CultureFilter.CultureId)'/>

</class>

Please note the Name property, we map it using a formula, which is just a piece of SQL that we put in the mapping. The new thing here is that we can refer to :CultureFilter.CultureId in the formula. Where does it come from? As you can see above, we need this to be transparent to the developer when using the code.

The secret is in the following bit of mapping:

<filter-def name='CultureFilter'>

       <filter-param name='CultureId' type='System.Int32'/>

</filter-def>

Here we define a filter, which takes parameters, usually a class will use the filter-def parameters to express a filter according to this parameters. But we don't want to filter the results, we want to just have it happen. It turns out to be that using the full name of the filter parameter is something that NHibernate understand anywhere, so...

//This is usually in Application_BeginRequest
session.EnableFilter("CultureFilter").SetParameter("CultureId", Thread.CurrentThread.CurrentCulture.LCID);

Product product = session.Get<Product>(5);
productName.Text = product.Name;

And you get exactly what you wanted! I was very pleased when I was able to come up with such an elegant solution :-D

Print | posted on Tuesday, December 26, 2006 7:21 PM

Feedback


Gravatar

#  12/26/2006 11:12 PM Grimace

Quite an elegant solution to a very common problem. I guess this one's a keeper for the NH docs!


Gravatar

#  12/26/2006 11:39 PM Bill Pierce

This one is definitely going in the toolbox.


Gravatar

#  12/28/2006 12:54 AM Pierre Henri

That's definitively a nice solution.
I recall that somebody talk about a similar problem a year ago on the forum; the solution he ended up with was running the queries manually.

BTW, this may not be a nice solution if this Name is not that often needed; in which case we will be executing this Select for nothing...


Gravatar

#  12/28/2006 7:21 AM Ayende Rahien

If name is not often needed, then the best scenario would be to move it to a many-to-one association, and eager load it when necessary.

It makes me wonder if this is another accidental feature like CreateFilter()


Gravatar

#  12/28/2006 8:59 AM Alessandro

Very cool!! is it possibile with ActiveRecord too?


Gravatar

#  12/28/2006 1:58 PM Ayende Rahien

At the moment, I don't think so.
There was some talk of adding it to AR, but I don't think that we implemented it yet.


Gravatar

# re: Localizing NHibernate: Contextual Parameters 2/12/2007 11:24 PM Tom

Nice... what happens with 2nd level caching? How do you distinguish between the same object in different languages?


Gravatar

# re: Localizing NHibernate: Contextual Parameters 2/12/2007 11:31 PM Ayende Rahien

Good question, I do not know off hand the answer.
I don't _think_ that it is taking into account the filters, but I can't tell for sure.
You can try it out and tell me if it doesn't work (via the NHibernate JIRA), we will fix it.

Comments have been closed on this topic.