Development With A Dot

Blog on development in general, and specifically on .NET



My Friends

My Links

Permanent Posts

Portuguese Communities

September 2011 - Posts

NHibernate Pitfalls: Fetch and Lazy

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

When we use the fetch mode join for an association, we are implicitly using eager fetching, not lazy. Because the SELECT statement for the root entity already includes the association table, it will be loaded at the same time, so, it makes no sense to have fetch=”join” and lazy=”false” for an association; in this case, fetch will take precedence and disable lazy loading. Be aware if your association has lots of entities, and you don’t need it most of the time.

On the other hand, for many to one associations that you don't want to load immediately, you must take other steps: set fetch to select, set lazy to either proxy or no-proxy, and also set the other endpoint entity as lazy, otherwise, no proxy will be generated for it and thus it can't be lazy loaded.

NHibernate Pitfalls: SELECT N + 1

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

One of the recurring situations when accessing a list of entities and changing them is the SELECT N + 1 problem; it is an anti-pattern that, although won’t throw exceptions, and in this sense can be considered to be harmless, means accessing your database in a less than optimal way: one query for the list items without any of its lazy associations (1) and one additional query for each of the list items’ lazy associations (N). Of course, this only happens if you have lazy associations at all.

The way to go over it, when you absolutely know that you will need to access all associations for all entities is to preload them when you issue the query. Here’s how:

   1: IEnumerable<Order> ordersWithLinq = session.Query<Order>().Fetch(x => x.Customer).FetchMany(x => x.Details).ToList();
   3: IEnumerable<Order> ordersWithHql = session.CreateQuery("from Order o inner join fetch o.Customer inner join fetch o.Details").List<Order>();
   5: IEnumerable<Order> ordersWithCriteria = session.CreateCriteria<Order>().CreateAlias("Customer", "c", JoinType.InnerJoin).CreateAlias("Details", "d", JoinType.InnerJoin).List<Order>();
   7: IEnumerable<Order> ordersWithQueryOver = session.QueryOver<Order>().Fetch(x => x.Customer).Eager.Fetch(x => x.Details).Eager.List<Order>();
NHibernate Pitfalls: Merge

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

Sometimes there may be the need to store a loaded entity in a place that outlives the session that retrieved it.

Later on, you need to associate this entity with a new session, in order to continue using it; for this, you tipically use the Merge method. When you do this, however, you must use a new reference to the entity returned by this method:

   1: Order order = HttpContext.Current.Session["order"] as Order;
   3: Order savedOrder = session.Merge<Order>(order);
   5: savedOrder.Customer = session.Load<Customer>(Int32.Parse(this.customerDropDownList.SelectedValue));

All future changes, such as setting property values, must be made to the merged entity, not the saved one, because NHibernate knows nothing about it:

   1: Boolean contains = session.Contains(order);                    //false
   2: Boolean containsSavedOrder = session.Contains(savedOrder);    //true

Now you just have to Flush your session (or, better yet, commit your transaction) and the entity will be updated in the DB. Beware, though: if you don't set ALL of your entities' properties, the record in the database will be overwritten with the current values in this instance!

Workshop Novidades ASP.NET 4.0

No próximo dia 8 de Outubro irei dar um pequeno workshop, de 3 horas, nas instalações da Flag, em Coimbra:


Se tiverem curiosidade, apareçam! Winking smile

NHibernate Pitfalls: Lazy Properties and Closed Sessions

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

Beware when accessing previously un-accessed lazy properties (simple properties, many to one, one to one, many to many, one to many) outside the scope of its originating session: it will result in an exception, because the session is no longer accessible.

One place where this commonly occurs is when returning entities from WCF web services and there are two ways to get over this:

  • Force loading of all associations beforehand;
  • Instantiating a custom Data Transfer Object with all required properties loaded.
   1: //load an entity with all of its associations
   2: var customer = session.Query<Customer>().Fetch(x => x.Orders).Where(x => x.Id == CustomerId).Single();
   4: //load an entity into a custom data transfer object
   5: var customerDTO = session.Query<Customer>().Where(x => x.Id == CustomerId).Select(x => new CustomerDTO(x)).Single();                
NHibernate Pitfalls: Entity Refresh

This is part of a series of posts about NHibernate Pitfalls. See the entire collection here.

Whenever an entity is loaded from NHibernate, either by its direct id, by LINQ, Criteria API, Query Over or HQL, it is stored in the first level cache. After that, whenever the row that this entity refers to is loaded by a query, the same entity is returned exactly as it is, that is, any eventual DB updates are ignored.

The only way to get DB updates to the loaded entity is to call Refresh, Lock or Merge, but you can also get current individual column values if you select them explicitly.

   1: //entity is loaded into first level cache
   2: var c1 = session.Get<Customer>(CustomerId);
   4: //same entity is returned
   5: var c2 = session.Query<Customer>().Where(x => x.Id == CustomerId).Single();
   6: Debug.Assert(Object.ReferenceEquals(c1, c2));
   8: var c3 = session.CreateQuery("from Customer c where c.Id = :id").SetParameter("id", CustomerId).UniqueResult<Customer>();
   9: Debug.Assert(Object.ReferenceEquals(c1, c3));
  11: var c4 = session.CreateCriteria<Customer>().Add(NHibernate.Criterion.Expression.IdEq(CustomerId)).UniqueResult<Customer>();
  12: Debug.Assert(Object.ReferenceEquals(c1, c4));
  14: var c5 = session.QueryOver<Customer>().Where(x => x.Id == CustomerId).SingleOrDefault();
  15: Debug.Assert(Object.ReferenceEquals(c1, c5));
  17: //get updates from the DB
  18: session.Refresh(c1);
  20: //get the current value from the DB
  21: var n2 = session.Query<Customer>().Where(x => x.Id == CustomerId).Select(x => x.Name).Single();
  23: var n3 = session.CreateQuery("select Name from Customer c where c.Id = :id").SetParameter("id", CustomerId).UniqueResult().ToString();
  25: var n4 = session.CreateCriteria<Customer>().Add(NHibernate.Criterion.Expression.IdEq(CustomerId)).SetProjection(NHibernate.Criterion.Projections.Property("Name")).UniqueResult<String>(); ;
  27: var n5 = session.QueryOver<Customer>().Where(x => x.Id == CustomerId).Select(x => x.Name).SingleOrDefault<String>();
More Posts