Larry Zhao

co-founder of jianshu.com, developer, game lover and soccer fan.

Lazy-load problem in Castle activerecord

Castle activerecord is from castle team. I think most of you will find it quite a nice ORM framework in C# world, despite it’s documents are not well organized.

During using Castle activerecord, there is a very buggy problem related to lazy load. And I found that there’s plenty of people who’s suffering from it.

Problem:
The problem is, if you mark up a relationship (or domain model) as lazy like following:

Public class Category{

...

[HasMany(typeof(Post),Table="Posts", ColumnKey="blogid", Lazy=true)]
public IList Posts
    {
         get { return posts; }
         set { posts = value; }
    }
}

And when you query the list of Posts from a Category instance like:
IList posts = category.Posts;

then sometimes you will get an error like:

failed to lazily initialize a collection of role: Category.Posts, no session or session was closed.

And this problem is because the object instance you are using to query is not connected to an valid session right now, so it’s impossible for the framework to query the database for the lazy part.

Castle activerecord as a framework of activerecord which hides the detail of nHibernate session, so that this seems quite abrupt to us. I am not going to details about why it happens, To know more about that I recommend two posts:

Solutions:
Actually we have more than one way to figure our way out. I am using Castle activerecord in web application so this is also web- oriented.

First, we could follow the approach suggested in Jakob’s post, to use ISession.lock every time we query a Lazy-Load property, like:

ISession session = holder.CreateSession(typeof(Category));
session.Lock(category, NHibernate.LockMode.None);
IList posts = category.Posts;
holder.ReleaseSession(session);

It works, but it’s quite a lot of code, and you also need to release the session every time, otherwise you are going to get an error.

Second, we could wrap the code querying Lazy-Load properties like below:

using(new SessionScope){
  IList posts = category.Posts;
}

This is from document from castleproject.org. It seems promising, but I haven’t tried it.

The last solution is always the best, if you read document from castleproject.org carefully, you will find they’ve already given a way to make SessionScope per request, which you could imagine that the object always finds a session to attach to.

One of the way to implement that is to add the following line to your webconfig.xml if you are using Castle activerecord 2:

<system.web>
    <httpModules>
        <add name="ar.sessionscope" 
            type="Castle.ActiveRecord.Framework.SessionScopeWebModule, Castle.ActiveRecord" />
    </httpModules>
</system.web>

And if you are using Castle activerecord 3, you’d be doing this configuration like:

<system.web>
    <httpModules>
      <add name="ARScope" type="Castle.ActiveRecord.Framework.SessionScopeWebModule, Castle.ActiveRecord.Web"/>
    </httpModules> 
</system.web>

And in official document, there’s another way to achieve it by extending the HttpApplication class, you could check it out here.

comments powered by Disqus