简体   繁体   中英

Lazy loading not working with NHibernate

Using lazy="true" in my classes causes my application to work perfectly, but the performance is horrible. I turned this on back when I was creating this from a tutorial and just wanted to get something working as quickly as possible. (I used this tutorial: http://geekswithblogs.net/BobPalmer/archive/2010/04/23/mapping-object-relationships---quickstart-with-nhibernate-part-3.aspx which was very helpful at getting something that worked quickly)

I don't need it to load all of these many-to-one classes when I just need to use the one object, so it made sense to turn lazy loading back on. Then, I looked into the objects and saw nothing but exceptions for those many-to-one classes inside my main objects. When I try to use those properties later I get the following error:

"Could not initialize proxy - no Session." 

I'm guessing this means that the session is closed, so it fails when trying to lazy-load the additional objects. My session provider looks like this (same as the tutorial):

class SessionProvider {
    private static ISessionFactory _sessionFactory;
    private static Configuration _config;

    public static ISessionFactory SessionFactory {
        get {
            if (_sessionFactory == null) {
                _sessionFactory = Config.BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }

    private static Configuration Config {
        get {
            if (_config == null) {
                _config = new Configuration();
                _config.AddAssembly(Assembly.GetCallingAssembly());
            }
            return _config;
        }
    }
}

Which is then used by my repositories like this:

using (var session = GetSession()) { ... }

Which gets the session from this function:

private static ISession GetSession() {
        return SessionProvider.SessionFactory.OpenSession();
}

So my question is, what am I expected to do here? Keep the session open? Make it static across all repositories? I don't have enough experience with NHibernate to understand how this works yet. My priority right now is only reading from the database, if that makes any difference. This is going in a code library that will eventually be used both on our website and various C# .Net apps.

The recommended approach is to use the unit of work pattern . Another option for you would be to use eager loading if you know you're going to need specific entities up front.

You are micromanaging your session by opening and closing it in the repository. Not only it breaks lazy loading, it hurts performance a lot.

Instead, session management should be done with a more coarse-grained approach.

For example, for web applications, one recommended pattern is session-per-request (the session is opened and closed by a module or global handler, and bound to the HttpContext)

Just look up session-per-request, there are a lot of examples.

You don't say whether your application is a web app (ASP.NET MVC for example) or a console/GUI/service app. A common problem with lazy-loading ORMs in MVC web apps is that the session is closed at the end of the controller's action method, the proxy entity object is passed to the view which then tries to enumerate some lazily-loaded collection which fails due to the session having been disposed. The solution to this is called the 'Open Session in View' pattern (in the Java world at least) which generally involves using a filter to open a session at the beginning of a web request, ensure that session is passed to any controller or other object requesting it (this would generally be managed by your IoC container), then the session is still active when the view is executing and lazy-loads properties.

Having worked with Hibernate on both Java and .Net in a few systems, I'm generally of the opinion that lazy loading is bit of a curse. Whilst it is handy it frequently requires additional plumbing (such as something to support the 'Open Session in View' pattern), and frequently kills performance. It is probably better to use eager loading for those collections that you know are highly likely to be accessed. Analysing which collections need to be eagerly loaded forces you to better understand the data access profile of your app which will help you understand the performance characteristics of the application much better, which can only be a good thing!

The simple problem is that you're trying to access yet-to-be-lazy-loaded values when the entity's associated session has been disposed.

NHibernate's Session object is intended to follow a unit-of-work pattern. That is to say, you create one session for every unit you want to complete.

Speaking plainly, you must keep the Session instance alive (not disposed) until you are "done" with the objects associated with that session. This means that your session object becomes the scope of your work:

using(var session = GetSession())
{
    // Load some objects.
    // Manipulate some values.
    // Commit changes.
}

An NHibernate session must be treated similarly to a database transaction, where all steps required to complete the broader operation must be completed within the scope of the session.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM