I'm trying to implement multi-tenancy in an ASP.NET MVC application I have, that uses NHibernate. Though I have control over the database for multi-tenancy. I'm trying to figure out the best way to filter our database queries using NHibernate.
I would like to know if there is a painless way where I can append a condition (something like WHERE InstanceID = 1
) to every CRUD query to the DB using NHibernate.
I looked at global filters. But I wasn't sure if I'm using it the right way. I tried something like this.
NHibernateSession.GetDefaultSessionFactory().GetCurrentSession()
.EnableFilter("instance-filter").SetParameter("InstanceId", "2");
But it didn't seem to work. Any good example of NHibernate global filters / any good approach of filtering all DB queries with a condition would be highly appreciated.
I've been looking for the same thing for a small project of mine that's still in planning phase. The most complete implementation of using a single database that I came upon is written by Michael Valenty in his blog post: Bolt-on Multi-Tenancy in ASP.NET MVC with Unity and NHibernate: Part II – Commingled Data. He's also using global filters.
Just for the sake of completeness, here are the mappings he used:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<filter-def name="tenant">
<filter-param name="id" type="System.Int32" />
</filter-def>
</hibernate-mapping>
And for each entity:
<class name="User" table="[user]">
<id name="Id" column="user_id">
<generator class="identity" />
</id>
<property name="Username" />
<property name="Email" />
<filter name="tenant" condition="tenant_id = :id" />
</class>
After that, he uses his IoC container of choice to inject the parameter value to instance of ISession.
session.EnableFilter("tenant").SetParameter("id", c.Resolve<Tenant>().Id);
There's also an interceptor to implement - to write the value of current tenant id when saving the entity (OnSave
method), and also to check whether the given entity belongs to current tenant when loading the entity by id (OnLoad
method). OnLoad
override is necessary because tenant filter won't be applied when loading entity by id.