Retrieving entities by identifiers

If you recall the unit tests that we wrote in the previous chapters, we commonly used a method named Get<T> on ISession to retrieve a persistent instance of an entity by its id. Following is relevant part of that code:

using (var transaction = session.BeginTransaction())
{
  var employee = session.Get<Employee>(id);
  transaction.Commit();
}

You might be wondering what the reason behind providing this method is. All we are doing is querying an entity by its primary key. Why not use criteria query, QueryOver, or LINQ to query entities by primary key? Well, you can use those methods when you are not querying by identifier, but if you are querying by identifier then Get<T> has some optimizations built in that make it a preferred choice over any other querying method. Even other querying methods will use Get<T> internally if they need to load an entity by identifier.

Get<T> will first check whether an entity is present in the identity map of the session, and it would then check the second level cache for existence of the entity there. If an entity is not found in both identity map and second level cache, then it issues SQL to fetch the entity from database. Do you see the difference? There is an element of optimization that Get<T> applies that other querying methods do not. If there is a chance that an entity is present in first or second level cache then you can save a database roundtrip and few CPU cycles by using Get<T>.

ISession has another method named Load<T> which is syntactically same as Get<T> with a subtle difference in how it works internally. Get<T> would go from first level cache to second level cache to database looking for the entity and if not found, it returns a null. On the other hand, Load<T> works similarly to how lazy loading works. Load<T> does not hit the database immediately. It only returns a proxy of the entity with its identity set to the one passed to it. If you try to access any other properties of the entity returned by Load<T> then actual loading of the entity from database is triggered. Because NHibernate does not check if the id that you passed to Load<T> is present in the database or not, it is possible to pass non-existing id to Load<T> and still get a proxy entity back. When NHibernate tries to load this entity from database, it figures out that there is no record in database with that id and throws NHibernate.ObjectNotFoundException.

Why use Load<T>?

By the sound of its behavior, Load<T> does not seem to add much value over Get<T>. But there are some situations where Load<T> results in more optimized behavior than Get<T> does. When an employee becomes member of a community, you may write code as follows:

object employeeId =null; //This is passed from some other code
var communities = Database.Session.Query<Community>()
        .First(c => c.Name == "London bikers");

var employee = Database.Session.Get<Employee>(employeeId);
communities.AddMember(employee);

In the preceding code, the employee instance loaded using Get<T> is not used for anything other than setting the relationship between Employee and Community. If we know the ID of the employee, then all we need in order to setup the relationship is a proxy employee instance with its id set. Load<T> offers exactly that. We could save an additional database roundtrip by using Load<T> in such similar situations.

So to summarize:

  • Use Get<T>/Load<T> over querying by primary keys. Get<T>/Load<T> is optimized.
  • If you need to access properties of the loaded entity (other than ID) then use Get<T>.
  • If you do not intend to access properties of the loaded entity but only use it as a proxy to satisfy an association then use Load<T>.

Note

If entity is not marked as lazy, then Load<T> and Get<T> do not behave differently. Calling Load<T> for non-lazy entities results in immediate loading of the entity. This is because NHibernate never proxies entities marked as non-lazy.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.171.153