Wednesday 27 July 2011

Mixed mappings with HBM and mapping by code

Are you ready to embrace and upgrade to NHibernate 3.2 but are unsure about converting all your project hbm.xml files to the new mapping by code syntax?

It is possible to mix and match; that is use your current projects xml files and start to use the mapping by code. So if you find yourself in this position:-
I am ready to start using NHibernate 3.2 in an old project and utilise all the goodness that the mapping by code syntax gives me, but I am not quite ready to move over all my xml files yet.
I realise that one could argue about testing, that is if all my tests cover mappings then the conversion should not cause friction. However some of us are left with named queries right?

The solution is quite simple, first you need to define your web.config (or external hibernate config file) as:-
  <section name="hibernate-configuration"
   type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"     requirePermission="false" />

    <property name="dialect">
    <mapping assembly="Domain.Model" />
Then in your start up code:-
var mapper = new ModelMapper();
//Notice the .Configure, this is the magic that allows you to
//  use the mixed mappings
var configure = new Configuration().Configure();
configure.DataBaseIntegration(x =>
  x.ConnectionStringName = "db";

configure.AddDeserializedMapping(mapping, "Domain");
SessionFactory = configure.BuildSessionFactory();
If you notice we are making a call to .Configure. Because of this call then NHibernate always needs a hibernate-configuration section inside your web.config. We are therefore instructing NHibernate to configure the mappings as usual in web.config and then configure using the new mapping by code syntax (which is based on loquacious).

After you have converted your XML you can remove all references in web.config and change:-
var configure = new Configuration().Configure();
to just:-
var configure = new Configuration();

I personally have used this technique as I still have a few named queries left over in my XML files and have not yet moved them out, therefore this mixed mapping has really helped me out.

You got to love the simplicity of being able to mix old and new, well done to the NHibernate team for taking away some pain points.

Monday 25 July 2011

View the xml generated when mapping by code

When using mapping by code it may not be 100% obvious what XML is being generated for NHibernate.

You have a couple of options, the first option is to tell NHibernate to write all your mappings into the bin folder:-
var mapper = new ModelMapper();

//This will write all the XML into the bin/mappings folder

The second option is to use the extension method .AsString(). However you will be presented with one large XML blob.
var mapper = new ModelMapper();
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
//you could add a breakpoint here! 
var mappingXml = mapping.AsString();
Either way you will be able to quickly and easily view the XML that is being injected into NHibernate. This is very useful for debugging or if you are upgrading all your hbm.xml files into the new mapping by code syntax and wish to compare like for like.

Wednesday 20 July 2011

Mapping conventions using mapping by code

I asked the following question on the NHUsers google group; "I am playing with mapping by code and started wondering about conventions."

According to WIKI:-
Conventions may be formalized in a documented set of rules that an entire team or company follows, or may be as informal as the habitual coding practices of an individual.
All of my mapping files share the same common functionality:-

1. All Bags have bag.Cascade(Cascade.All | Cascade.DeleteOrphans) and bag.BatchSize(10)
2. All table names are lowercase Table("organisation");
3. All bags have the action => action.OneToMany()

Normally in NHibernate 3.2 I would define all my mapping classes using this type of syntax:-
internal class OrganisationMapping : ClassMapping<Organisation> {
  public OrganisationMapping() {
    Id(x => x.Id, map => map.Generator(Generators.GuidComb));
    Property(x => x.Name, x => x.NotNullable(true));

    Bag(x => x.IncomeTypeList, bag => {
      bag.Key(k => k.Column(col => col.Name("OrganisationId")));
      bag.Cascade(Cascade.All | Cascade.DeleteOrphans);
    }, action => action.OneToMany());
The problem I have is that I have 20 or so of these class mappings to do and most of my bags share similar properties. This is what led me to think about conventions and a way to make my code a) compact, b) follow similar standards and c) allow me to map my classes quicker without having to think about what I had set before.

So how do we solve this problem? First I read this blog post from Fabio Maulo then I decided to have a go.

Below is a snippet of my configuration code:-
var mapper = new ModelMapper();

var configure = new Configuration();
configure.DataBaseIntegration(x => {
  x.ConnectionStringName = "db";

var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
configure.AddDeserializedMapping(mapping, "Domain");

SessionFactory = configure.BuildSessionFactory();
The first problem is how do I make all my bags have a batch size of 20 and set Cascade.All | Cascade.DeleteOrphans

This is really easy I just need to set a convention before my bag gets mapped:-
mapper.BeforeMapBag += (mi, t, map) => {
  map.Cascade(Cascade.All | Cascade.DeleteOrphans);
Note: this code gets inserted just after var mapper = new ModelMapper();

The second problem is how do I make all my table names lowercase. Again this convention is extremely easy to achieve:-
mapper.BeforeMapClass +=
  (mi, t, map) => map.Table(t.Name.ToLowerInvariant());
mapper.BeforeMapJoinedSubclass += 
  (mi, t, map) => map.Table(t.Name.ToLowerInvariant());
mapper.BeforeMapUnionSubclass += 
  (mi, t, map) => map.Table(t.Name.ToLowerInvariant());
Note: If you are using subclassess in your NHibernate project then you will also need to define the convention here as well.

The third problem is how do I remove the need to have action => action.OneToMany() defined on all my mappings. If you do not specify this action then you mapping files will contain:-
<bag name="IncomeTypeList" cascade="all,delete-orphan" batch-size="20">
  <key column="Id" />
  <element type="Domain.Model.Entities.IncomeType, Domain.Model" />
but what we really want is:-
<bag name="IncomeTypeList" cascade="all,delete-orphan" batch-size="20">
  <key column="Id" />
  <one-to-many class="IncomeType" />
This one is a little trickier to achieve. According to Fabio:-
We can’t define the one-to-many relation using the ModelMapper because the responsibility to define/discover all relation is delegated to the IModelInspector injected to the ModelMapper instance
So lets look at the solution, first we need to define a new ModelInspector that inherits from ExplicitlyDeclaredModel then override the IsOneToMany method:-
public class MyModelInspector: ExplicitlyDeclaredModel
    public override bool IsOneToMany(MemberInfo member)
            return true;
        return base.IsOneToMany(member);
To use our ModelInspector we need to slightly change the configuration code so we pass the ModelInspector into the ModelMapper:-
var modelInspector = new MyModelInspector();
var mapper = new ModelMapper(modelInspector);
and viola all works as expected!

It should also be pointed out that all these conventions can be overridden by explicitly specifying your requirements on the individual classes themselves.

I have to say at first when I looked at this mapping by code syntax it looked scary and complex. However after a couple of hours playing with it and actually coding my application I have found it quite pleasing and fairly simple (with help from Fabio). I am now looking forward to mapping my domains using this syntax rather than the tried and trusted xml that I have used in the past.

If you haven't already looked at the mapping by code (new in NHiberbate 3.2) then I suggest you do as it really is cool and according to Fabio sexy, but that is his personal taste!

Monday 18 July 2011

Lazy loading and the select n+1 problem

With Nhibernate Lazy loading comes switched on by default (in fact it has since version 1.2). However there are some things that you need to understand, lazy loading only works as long as the session is open and one of the side effects is that you may hit the select n+1 problem.

To see this problem in action then let's say you have a collection of Movies and each Movie has a collection of Actors. In other words, Movie to Actor is a one-to-many relationship.

Now, let's say we get the first 5 Movies and then for each iteration we need to show all the actors.

The following SQL would be sent to the server:-
SELECT * FROM Actor WHERE MovieId = 1;
SELECT * FROM Actor WHERE MovieId = 2;
SELECT * FROM Actor WHERE MovieId = 3;
SELECT * FROM Actor WHERE MovieId = 4;
SELECT * FROM Actor WHERE MovieId = 5;
In other words, you have one select for the Movies, and then N additional selects for actors. As you can see you would be forced to hit the database 6 separate times, this is in essence is the select n+1 problem.

NHibernate overcomes this problem by allowing you to specify the batch-size in your mapping files. To see this in actual we are going to use the following domain:-
public class Movie : Entity
  public virtual string Director { get; set; }
  public virtual IList<actor> ActorList { get; set; }

public class Actor : Entity
  public virtual string Name { get; set; }
  public virtual string Role { get; set; }
All you need to do is to place the batch-size attribute to the bag definition.

  <key column="MovieId" not-null="true" />
  <one-to-many class="Actor" />

This now will produce the following SQL that only hits the database twice:-
SELECT * FROM Actor WHERE MovieId IN (1,2,3,4,5);
If we set a batch-size of 4 we would would hit the database three times:-
SELECT * FROM Actor WHERE MovieId IN (1,2,3,4);
SELECT * FROM Actor WHERE MovieId=5;
This is a real sweet spot that NHibernate provides for you out the box. I think is a real testament to some of the power NHibernate gives you allowing you to fine tune your code with minimal fuss. Of course for a simple model you could use eager loading but for models with lots of properties (database columns) then this might not be desirable. I for one prefer choice and this reinforces the sweetness that comes with NHibernate.

Entity framework (as of 4.2) cannot solve this problem out of the box and one approach is to use .Include("Actors"), however this eager loads all of your entities and will produce a cartesian product that can send a lot of duplicated data down the wire. I suspect though the Microsoft team will tidy up lazy loading some time in the future.

Friday 15 July 2011

Using eager loading can produce unexpected results

When using NHibernate for the first time you sometimes decide that you want to utilise Eager loading for some of the following valid reasons.
  1. You want to make only one trip to the database
  2. Your result set is only small, contains 10 or so rows and only a small number of columns
Therefore you have decided to embark on the Eager loading journey however one of the side effects is that the generated SQL always returns a cartesian product.

According to WIKI A Cartesian product (or product set) is the direct product of two sets.

The SQL snippet below shows us that duplicate movies will be returned for every actor in the table. This can be quite clearly seen:-
 Product p
left outer join
 ActorRole a on p.Id = a.MovieId

So now we are armed with this knowledge how does NHibernate handle the cartesian product? I have the following model, I have a one-to-many join on actors. The domain model looks like this:-
public class Movie : Entity
  public virtual string Director { get; set; }
  public virtual IList<Actor> ActorList { get; set; }

public class Actor : Entity
  public virtual string Name { get; set; }
  public virtual string Role { get; set; }
When using NHibernate and QueryOver we would specify eager loading by using:-
return Session
  .OrderBy(w => w.Name).Asc
  .Fetch(f => f.ActorList).Eager
However this produces the following result:-
As you can clearly see NHibernate hydrates all movies (root entities) regardless whether they are duplicated or not. So how do we stop this happening? One way is to tell Nhibernate that we are only interested in hydrating distinct movies, e.g.
return Session
 .OrderBy(w => w.Name).Asc
 .Fetch(f => f.ActorList).Eager
Notice that we are specifying a result transformation which tells NHibernate to only hydrate the root enities once per actor. Now we get the desired result.

It should be noted that the duplicates are removed (well probably not added in the first place) in client code, but you should remember that you are returning duplicate movies from your SQL.

Also it should be noted that this only works for single joins. If you are joining two tables or more then you should read this article by Ayende.

Thursday 14 July 2011

QueryOver with multiple joins into a DTO

Consider the following NHibernate HQL. As you can see it is joining three tables and pulling back three columns from two different tables.

The [Organisation] table joins to the [OrganisationType] which in turn joins to the [IncomeType] table.
hql = @"
  t.Name, ot.LocalRespend, ot.LocalRespendOutArea
  organisation o 
 inner join
  organisationincometype ot on ot.OrganisationId = o.Id
 inner join
  incometype t on ot.IncomeTypeId = t.Id
  o.Id = :orgId and t.Id = :incomeTypeId;";
How would you do the following using the QueryOver syntax?

Simple when you know how:-
var oit = null;
var  it = null;

return Session.QueryOver<organisation>()
   .Where(w => w.Id == orgId)
  .JoinAlias(r => r.OrganisationIncomeTypeList, () => oit)
  .JoinAlias(r => oit.IncomeTypeList, () => it)
   .Where(() => it.Id == incomeTypeId)
   Projections.Property(() => it.Name),
   Projections.Property(() => oit.LocalRespend),
   Projections.Property(() => oit.LocalRespendOutArea)
The secret is that we are using JoinAlias into temporary local variables and then projecting these aliases into our DTO. The beauty of using the QueryOver syntax is that we are losing all the magic strings that we would get with HQL or Criteria.