Logo

NHibernate

The object-relational mapper for .NET

How to

This page is converted from the old nhforge.org Wiki. First published by: Germán Schuager on 02-12-2009, Last revision by: Germán Schuager on 03-30-2011

Contextual data using NHibernate filters

I'm in the middle of the development process of an application using NH for data access, and I'm faced with a requirement that could be stated as follows:

The application needs to provide support for different Contexts of execution, and certain entities must be context-aware, which means that at a given time, the application only sees instances of these entities that correspond to the current context of execution.

Now, just remember that I have several entities defined that are used throught the entire application layer stack, so I wanted to solve this issue modifying as little as possible.

I'm very proud with the solution that I came up with, and also very amazed by the power of NHibernate.

To simplify a little lets assume that I have a static class that defines the current context of execution:

public enum ContexType
{
ContextA,
ContextB,
}
public static class Context
{
public static ContextType Current { get; set; }
}

Then, I create an interface that will be implemented by all the entities that need to be contextualized:

public interface IcontextAware
{
ContextType Context { get; set; }
}

Given a Cat class that needs to be contextualized, then I add the property to the class and to the mapping:

public class Cat : Entity, IcontextAware
{
...
ContextType Context { get; set; }
...
}
 
<class name="Cat">
...
<property name="Context">
...
</class>

The idea now, is to use the dynamic filtering capabilities of NHibernate to only retrieve the Cats instances corresponding with the current context every time that a query against Cat is issued.
Typically this means that I need to add a filter definition to the mappings and the specify the condition for that filter in every class mapping that need to be aware of this behavior.
But there is an easier way to do this automatically:

var filterParametersType = new Dictionary<string, Itype>(1);
filterParametersType.Add("current", NhibernateUtil.Enum(typeof(ContextType)));
cfg.AddFilterDefinition(new FilterDefinition("contextFilter", ":current = Context", filterParametersType));

foreach (var mapping in cfg.ClassMappings)
{
if (typeof(IContextAware).IsAssignableFrom(mapping.MappedClass))
{
mapping.AddFilter("contextFilter", ":current = Context");
}
}

Just do this (cfg is the NH Configuration object) before building the session factory and it creates the correct filter definition and adds the condition to every entity mapped that implements IContextAware.

At this point we just have our filter defined; now we need to enable it in order to actually filter something. It would be very handy if we can enable filtering at session factory scope, but since the session factory is immutable we need to enable it for each session that we will be using.

Wait.... maybe something else can do this work for us...

The following interceptor actually takes care of 2 things:
1. enables the context filter as soon as it is attached to the session, and
2. assigns the correct value to the Context property of entities implementing IContextAware when they are persisted.

public class ContextInterceptor : EmptyInterceptor
{
public override void SetSession(ISession session)
{
session.EnableFilter("contextFilter").SetParameter("current", Context.Current);
}

public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
var contextAware = entity as IContextAware;
if (contextAware != null)
{
int index = Array.Find(propertyNames, 0, x => x.Equals("Context"));
state[index] = contextAware.Context = Context.Current;
return true;
}
return false;
}
}

Every session in the application needs to be created specifying this interceptor, but this should be an easy change (that depends on your architecture) if you are doing things right.

And thats all, the rest of the application is untouched and the requirement is fulfilled in a very elegant way.

(the original article is here)

© NHibernate Community 2024