Click here to Skip to main content
15,867,906 members
Articles / Programming Languages / C#

Comparing Transparent Lazy Loading between NHibernate and Entity Framework

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
14 Jan 2012CPOL7 min read 35.6K   920   23   2
In this article, I will explain what transparent lazy loading is and how it's implemented in NHibernate and Entity Framework.

Motivation

When I was trying Entity Framework after many years with NHibernate, I assumed the Entity Framework has the same transparent lazy loading behavior like NHibernate. But the assumption was not completely right. There are some minor behavior differences in Entity Framework. In this article, I will explain what lazy loading is, how NHibernate and Entity Framework implement it, and the similarities and differences.

What is Lazy Loading

Quote from Wikipedia:

Lazy loading is a design pattern commonly used in computer programming to defer initialization of an object until the point at which it is needed. It can contribute to efficiency in the program’s operation if properly and appropriately used. The opposite of lazy loading is Eager Loading.

When there is a domain model in an application, the entity in the domain model is used to represent the business entity in the problem domain. The association between the entities is equivalent to the relationship or interaction of business entities. The beauty of the domain model is it can be used to mimic the problem domain as much as we can. However, the domain object in memory is volatility. Hence, we need tables in the database to sustain the domain object. The domain object needs to be saved to the database or loaded back into memory from the database. The code for loading a domain object from the database is not trivial. Sometimes this kind of code uses the majority of developer time in application development. On the other hand, the code for loading a domain object is very similar for any domain entity. Therefore, an ORM framework is introduced to provide unified data access code. The ORM framework relieves the developer from doing the nitty-gritty database access code and let him focus on more important business logic code. By using an ORM framework, the domain object is loaded or saved with a developer friendly API.

The domain object in a domain model is not independent usually. In most situations, multiple domain objects are linked with each other as a big domain object graph. When the application gets a particular domain object from a database, we need to also load objects of related domain entities. Lazy loading is invented to load related domain objects on-demand. By doing this, the developer will directly work with the domain object and use it naturally without worrying how the related domain objects are loaded. The easiest way to support lazy loading is to have customized code in properties and methods to initialize a related domain object when it is going to be used. To add customized lazy loading code is a lot of work, time consuming, and error-prone. Fortunately, this kind of cross-cutting code can be injected by an AOP framework, such as the Windsor Castle AOP framework. This is called transparent lazy loading. The precondition for using transparent lazy loading is to make any property or method lazy loading friendly. This means marking a property or method as virtual in C#. The benefit of lazy loading is deferring query execution to the moment the application really needs it and reducing application memory usage.

How transparent lazy loading works in NHibernate

NHiberenate uses the Windsor Castle AOP framework to implement lazy loading. Let’s take a look at how to get lazy loading working in NHibernate:

  1. Make class properties and methods accessible and overload-able.
  2. Lazy loading can only be added for public or protected virtual properties or methods. Hence if your class properties and methods are private, you can’t use lazy loading. Also, lazy loading is not available on class fields.

    C#
    public class Employee
    {
        public virtual int EmployeeID { get; set; }
        public virtual string Name { get; set; }
        public virtual Iesi.Collections.Generic.ISet<Task> Tasks { get; set; }
    }
  3. Turn on lazy loading in the HBM mapping file.
  4. The lazy loading attribute is by default true, though you can still explicitly specify it in the HBM mapping file.

    XML
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Demo" namespace="Demo">
      <class name="Employee">
        <id name="EmployeeID">
          <generator class="identity" />
        </id>
        <property name="Name" />
        <set name="Tasks" lazy="true" inverse="true" cascade="all-delete-orphan">
          <key column="EmployeeID"/>
          <one-to-many class="Demo.Task"/>
        </set>
      </class>
    </hibernate-mapping>
  5. Return domain object from session.
  6. The domain object must be returned back from session, such as using the Session.Get method with the domain entity ID or using Session.CreateQuery with an HQL query.

    C#
    var employee = session.Get<Employee>(1);

    Or:

    C#
    var employee = 
      session.CreateQuery("from Employee as e where e.EmployeeID = 1"
      ).List<Employee>().FirstOrDefault();

    If the domain object is attached to the NHibernate session, you will not have lazy loading on it. Internally, NHibernate uses the Windsor Castle AOP framework to implement lazy loading. The Windsor Castle AOP framework subclasses a domain class and overrides all public and protected virtual properties and methods to provide logic for loading related domain objects during first time access. The subclass is named as <Class>Proxy.

    Task Object in NHibernate

    The Session.Get method is the place letting NHibernate create an instance of the subclass to replace the real domain object. The code using the created object is the same for the proxy object and the real domain object. The lazy loading process is transparent to the developer; if you want to know whether the object is loaded, you can use NHibernateUtil.IsInitialized to verify it.

    C#
    Assert.IsFalse(NHibernateUtil.IsInitialized(task.Employee));

    If you don’t want to have lazy loading enabled because of the N+1 performance issue or you know you will use all related objects in your application, you can turn lazy loading off by setting the lazy attribute to false.

    C#
    <set name="Tasks" lazy="false" inverse="true" cascade="all-delete-orphan">
      <key column="EmployeeID"/>
      <one-to-many class="Demo.Task"/>
    </set>

    By set lazy attribute to false, NHibernate will do implicate eager loading for you.

How transparent lazy loading works in Entity Framework

The earlier versions of Entity Framework didn’t have the transparent lazy loading feature. This was introduced in version 4.0. Because there is no transparent lazy loading design upfront, this feature in Entity Framework is designed differently to be backwards compatible with the Entity Framework older version. If you have POCO domain classes in your application, you are required to do the following to have lazy loading enabled in Entity Framework:

  1. Make class properties and methods override-able and accessible.
  2. This step is similar to NHibernate, Entity Framework uses a subclass to inject additional code into the domain class for lazy loading.

    Note: this only applies to the POCO style domain class; if your domain classes are generated with the EntityModelCodeGenerator template, it should already contain explicit lazy loading code, so no subclass is needed. Below is a code snippet generated with the EntityModelCodeGenerator template.

    C#
    [XmlIgnoreAttribute()]
    [SoapIgnoreAttribute()]
    [DataMemberAttribute()]
    [EdmRelationshipNavigationPropertyAttribute("LazyLoadingModel", "FK_Task_Employee", "Task")]
    public EntityCollection<Task> Tasks
    {
        get
        {
            return ((IEntityWithRelationships)this).RelationshipManager.
              GetRelatedCollection<task>("LazyLoadingModel.FK_Task_Employee", "Task");
        }
        set
        {
            if ((value != null))
            {
                ((IEntityWithRelationships)this).RelationshipManager.
                   InitializeRelatedCollection<task>("LazyLoadingModel.FK_Task_Employee", "Task", value);
            }
        }
    }
  3. Specify true for “Lazy Loading Enabled” in edmx properties.
  4. You can actually specify Lazy Loading Enabled in your context class directly. However, the default place is in the edmx file. The Lazy Loading Enabled setting will eventually get into your context class if it’s automatically generated, like LazyLoadingEntities in my example.

    C#
    public partial class LazyLoadingEntities : ObjectContext
    {
        public const string ConnectionString = "name=LazyLoadingEntities";
        public const string ContainerName = "LazyLoadingEntities";
    
        #region Constructors
    
        public LazyLoadingEntities()
            : base(ConnectionString, ContainerName)
        {
            this.ContextOptions.LazyLoadingEnabled = true;
        }
    
        public LazyLoadingEntities(string connectionString)
            : base(connectionString, ContainerName)
        {
            this.ContextOptions.LazyLoadingEnabled = true;
        }
    
        public LazyLoadingEntities(EntityConnection connection)
            : base(connection, ContainerName)
        {
            this.ContextOptions.LazyLoadingEnabled = true;
        }
    
        #endregion
    
        #region ObjectSet Properties
    
        public ObjectSet<Employee> Employees
        {
            get { return _employees  ?? 
              (_employees = CreateObjectSet<employee>("Employees")); }
        }
        private ObjectSet<employee> _employees;
    
        public ObjectSet<task> Tasks
        {
            get { return _tasks  ?? (_tasks = CreateObjectSet<task>("Tasks")); }
        }
        private ObjectSet<task> _tasks;
    
        #endregion
    }
  5. Return domain object from context.
  6. The domain object that needs to be lazy loaded must be returned from the context. A lazy loaded object looks like this in the Visual Studio 2010 Watch window:

    Task Object in Entity Framework

    If you don’t want to have lazy loading in Entity Framework, you can turn it off by setting LazyLoadingEnabled to false.

    C#
    this.ContextOptions.LazyLoadingEnabled = false;

    After lazy loading is turned off, Entity Framework will not load the related objects for you anymore. If you access a navigation property that is not loaded, you will get a NullReferenceException error. Or, when you access a navigation collection property that is not loaded, you will get an empty collection. This confuses me when I first use Entity Framework after years with NHibernate.

    Task Object in Entity Framework with Lazy Loading False

Caveat on Using Lazy Loading

In both NHibernate and Entity Framework, lazy loading is enabled by default. This is the best practice for most situations. By enabling lazy loading, the developer doesn’t need to worry how related objects are loaded. Also, untouched reference objects or collections do not occupy memory space. However, we need to be careful on the N+1 problem incurred by lazy loading in some situations. The N+1 problem is when you have lazy loading enabled for a domain object that has a collection property that contains N items, you will have one select query to be executed to retrieve data for the domain object itself, and then you will have every single query executed for each item in the collection. In total, you execute N+1 queries to get all data in the domain object and collection property instead of just one query. This causes too many queries to the database. To avoid the N+1 problem, we need to use a profiler monitor execution of the application to find the potential spot that has this problem, and then use explicit eager loading or batch loading to avoid or mitigate this problem.

Using the Code

The code is developed in Visual Studio 2010. You need to create database LazyLoading in SQL Server, and then run the attached CreateTables.sql script to create tables and data in it. Before running the code, remember to update the connection string in the configuration file to your local connection string.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Senior Software Developer from New Jersey, USA

Have 15+ years experience on enterprise application development with various technologies.

Comments and Discussions

 
QuestionDbContext and Code First Pin
Diego Vega14-Jan-12 8:02
Diego Vega14-Jan-12 8:02 
AnswerRe: DbContext and Code First Pin
Henry He14-Jan-12 16:23
Henry He14-Jan-12 16:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.