Thursday, 21 January 2010

Enabling Filters on mapped entities

I was trying to figure out if I could persuade NHibernate to enable filters on my <many-to-one /> entities and i fell on this discussion on the nhibernate-development google group. In short, <many-to-one /> filters should not work because filters where designed to filter data, not modify the domain.A valid viewpoint but i needed that functionality.

I have to think that filters should work out-of-the box and the developer should be responsible of enabling or disabling them. The reason is that i have a setup, that the represented domain can only reflected using filters. That is, applying WHERE clauses with dynamic parameters that are generated from the UI and that parameters remain the same for the length of the Request. As such the WHERE attribute on the class

Digging up the NH 2.1.2 source code i've found the single piece of code that is responsible for the behaviour.
Open up the NHibernate.Loader.Entity.EntityJoinWalker class and at approximately line 24 exists this piece of code:

SqlStringBuilder whereCondition = WhereString(Alias, uniqueKey, batchSize)
      //include the discriminator and class-level where, but not filters
      .Add(persister.FilterFragment(Alias, new CollectionHelper.EmptyMapClass<string, IFilter>()));
if you change it to

SqlStringBuilder whereCondition = WhereString(Alias, uniqueKey, batchSize)
       .Add(persister.FilterFragment(Alias, enabledFilters));

any enabled filters will be applied to your <many-to-one /> mapped entities as long as the filter exists on the entity definition mapping.

This little modification additionally enables filters for ISession.Get() and it works normally with lazy loading. If you are going to use ISession.Get() note that the cache is hit first before going to the datastore. In my setup, because the filter parameter defines a different entity instance for a given entity-id the second-level cache is not (and must not) be used, so take that into consideration before applying the modification to your solution.

I am thinking that if we could pass a parameter on the WHERE attribute on the class mapping (and ensure that this where fragment is used on ANSI-joins) none of this would have a reason to exist

Wednesday, 20 January 2010

Many-to-one and Composite-id mapping with NHibernate

Nhibernate's reference documentation sometimes leaves something to be desired. I was looking for the correct syntax to map a which has a and the documentation did not discuss it and a google search revealed nothing. While poking around the 'nhibernate-mapping.xsd' i found out that the element supports having elements underneath it so as to have the following syntax

<many-to-one name="Foo">
  <column name="FK_Column1">
  <column name="FK_Column2">

where the Foo class is mapped as
<class name="Foo" table="Foo">
  <composite-id class="FooCompId">
    <key-many-to-one name="SomeEntityA" column="PK_Column1">
    <key-many-to-one name="SomeEntityB" column="PK_Column2">

note here that the Foo class uses 2 properly mapped classes (SomeEntityA and SomeEntityB) hence the <key-many-to-one/>
interestingly i used this as part of a <subclass /> element and it worked normally.

And to map a <bag /> of "Foo" from an other entity this syntax worked

<bag name="Foos" inverse"true">
    <column name="FK_Column1">
    <column name="FK_Column2">
  <one-to-many class="Foo" />

the only requirement is that the order of the columns declared must match the order of the <key-many-to-one/> declared in the composite-id