ActiveRecord Flush Control

There are a lot of times in your application where you will load a lot of entities in a single session (to display a lot of data on a page, build a report, etc).  Since you are not planning on doing a mutable operation on an entity, there is a big saving that you can get from not flushing the session.  To get an idea on everything that happens when you do a flush, check out the AbstractionFlushingEventListener from the NHibernate source.

If you are using explicit SessionScope’s, not flushing the session is trivial:

   1: using (new SessionScope(FlushAction.Never))
   2: {
   3:     repository.DoSomeReadOperation();
   4: }

This will execute the operation without flushing the session at the end.

Another way to control this behavior is by using an interceptor.  For example, if you are using the Castle’s Automated Transaction Facility, you probably already have a way of determining whether or not your methods do mutable operations by their transaction attributes.  You can then use an interceptor to control flushing:

   1: public class UnitOfWorkInterceptor : IInterceptor
   2: {
   3:     private readonly ITransactionManager transactionManager;
   4:
   5:     /// <summary>
   6:     /// Initializes a new instance of the <see cref="UnitOfWorkInterceptor"/> class.
   7:     /// </summary>
   8:     /// <param name="transactionManager">The transaction manager.</param>
   9:     public UnitOfWorkInterceptor(ITransactionManager transactionManager)
  10:     {
  11:         this.transactionManager = transactionManager;
  12:     }
  13:
  14:     #region Implementation of IInterceptor
  15:
  16:     /// <summary>
  17:     /// Intercepts the specified invocation.
  18:     /// </summary>
  19:     /// <param name="invocation">The invocation.</param>
  20:     public void Intercept(IInvocation invocation)
  21:     {
  22:         ITransaction transaction = transactionManager.CurrentTransaction;
  23:         FlushAction flushAction = transaction == null ? FlushAction.Never : FlushAction.Config;
  24:
  25:         ActiveRecordUnitOfWork.Before(flushAction);
  26:         invocation.Proceed();
  27:         ActiveRecordUnitOfWork.After();
  28:     }
  29:
  30:     #endregion
  31: }

Note that in this example, interceptor ordering is important.  If you register the unit of work interceptor before the transaction interceptor in the ATM facility, then transactionManager.CurrentTransaction will always return null.  (The ActiveRecordUnitOfWork class in the above example is just a facade for controlling the session in a thread local as discussed in this post http://erichauser.net/2008/08/06/activerecord-session-scope-and-wcf-redux/).  If you do not want to tie your flushing to transactions, then you can always create your own metadata and read that metadata at runtime in the interceptor.  The ATM facility code is a great example of how to inspect metadata on initialization and use that metadata in your interceptor.

Hopefully, this little trick will some increased performance for your queries that return a lot of results.