Lesser-Known NHibernate Features: Filters
Unlike other OR/Ms – which, as always, shall remain unnamed – NHibernate offers a couple of ways to automatic filter results. Basically, we have two options:
- Static restrictions;
- Dynamic restrictions, or filters.
Because filters offer everything that static restrictions do and more, we’ll focus on filters.
A filter can specify a restriction, in terms of a SQL clause, to either an entity as a whole (the class, not a specific query) or to a collection (bag, set, list, map, array, etc).
For example, imagine you have a table that holds values that can be translated and a translation table for that purpose:
You will want to retrieve only the translation for the current culture. A domain model could look like:
We would like to apply a restriction to the Translations property of Translatable, so as to filter the translations by the current culture.
First, we need to create a filter, this is done at Configuration level:
cfg.AddFilterDefinition(new FilterDefinition("CurrentCulture", string.Empty, new Dictionary<string, IType> { { "Culture", NHibernateUtil.String } }, false));
The restriction can be defined on the filter itself, or per entity or collection. In this case, I didn’t specify it on the filter (string.Empty), so I will need to do it at the collection level:
mapper.Class<Translatable>(x =>
{
//rest goes here
x.Set(y => y.Translations, y =>
{
//rest goes here
y.Filter("CurrentCulture", z =>
{
z.Condition("Culture = :Culture");
});
});
}
);
A filter needs to be explicitly made active, and, if it contains parameters, all of its parameters must be set:
session.EnableFilter("CurrentCulture").SetParameter("Culture", CultureInfo.CurrentCulture.Name);
Now, whenever the Translations collection is retrieved, either through a SELECT or an INNER JOIN, the “Culture = :Culture” restriction - where, of course, :Culture is replaced by the current parameter value - will be applied automatically, together with the foreign key restriction.
The other option is to filter entities as a whole. Remember soft deletes? I wrote two posts on them (here and here). Instead of using static restrictions, we can instead use filters:
cfg.AddFilterDefinition(new FilterDefinition("SoftDeletes", "deleted = 0, new Dictionary<string, IType>(), true));
mapper.Class<Record>(x =>
{
x.Filter("SoftDeletes", y => {});
x.Set(y => y.Children, y =>
{
y.Filter("SoftDeletes", z => {});
});
});
In this example, I define the restriction string on the filter itself, and I apply it to both the Record entity and its Children collection. This time, no parameters, but we still need to enable the filter before we issue a query:
session.EnableFilter("SoftDeletes");
If for any reason you want to disable a filter, it’s easy:
session.DisableFilter("SoftDeletes");
Or even get its definition:
var filter = sessionFactory.GetFilterDefinition("SoftDeletes");
Enjoy your filters!