C# Corner

Leverage Lazy Loading in .NET 4.0

Lazy Loading is a programming pattern useful for resource-intensive objects.

Lazy loading is a common design pattern often used for constructing resource-intensive objects. It's  also frequently used in conjunction with the singleton, and/or factory patterns. Lazy loading entails constructing an object at the point it's needed, rather than when it's declared. If the object never ends up being used, it will never be fully constructed.

In versions of Microsoft's .NET Framework prior to 4.0, one would have to create their own lazy loading logic to take advantage of this pattern. Fortunately, .NET 4.0 includes Lazy<T>, which is a generic implementation of the lazy loading pattern. Let's first look at how to use Lazy<T> to implement the singleton pattern. The singleton pattern creates just one instance ever for an object; and it's often used for constructing objects that utilize scare resources.

Creating the Lazy Singleton

To create our lazy loaded singleton class, we create a static read-only Lazy<T> instance of our class. We also make the constructor private to prevent it from being called in a derived class. An instance of the class will only ever be created if the Instance property of the class is accessed. We leverage Lazy<T> to ensure this behavior. The Lazy<T> class defers initialization of its class until the .Value property is accessed. This particular implementation of the lazy initialization is known more commonly as the value holder pattern. The Lazy<T> Value property is a place holder that is only loaded at the point it is invoked.

namespace VSMLazyLoading.Common
{
    public class Singleton
    {
        private static readonly Lazy m_instance = new Lazy(() => new Singleton());

        public int MyProperty { get; set; }

        private Singleton()
        {
        }

        public static Singleton Instance
        {
            get
            {
                return m_instance.Value;
            }
        }
    }

Alternate Lazy Singleton

The Lazy<T> class also allows for similar behavior out-of-the-box  through use of an overloaded constructor. For example, say we have a ServiceProxy class used to consume a service endpoint that we only ever want to initialize once in a thread-safe manner.

First, we create our fake service proxy class.

namespace VSMLazyLoading.Common
{
public class ServiceProxy
{
public ServiceProxy()
{
Console.WriteLine("Service proxy created on {0}", DateTime.Now);
}

        public string SayHello(string name)
{
return String.Format("Hello, {0}", name);
}
}
}

Next we construct our lazy loaded service proxy class by utilizing the Lazy<T> class's ability to specify a value holder function and execution method. The LazyThreadSafetyMode.ExecutionAndPublication flag will ensure that only one thread may initialize the object and that only one instance is ever created.

Lazy<ServiceProxy>  proxy = new Lazy<ServiceProxy>(() => new  ServiceProxy(), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
Console.WriteLine(String.Format("Lazily loaded ServiceProxy on {0}", DateTime.Now));
System.Threading.Thread.Sleep(1000);
string message =  proxy.Value.SayHello("Eric");
Console.WriteLine(String.Format("Message received: {0}", message));
message = proxy.Value.SayHello("John");
Console.WriteLine(String.Format("Message received: {0}", message));
Console.ReadLine();

[Click on image for larger view.]
Figure 1. Creation of the ServiceProxy class.

As you can see in Figure 1, the ServiceProxy class was only constructed at the point at which the SayHello method was first invoked.

Simple Object Lazy Loading

Often, you may not need a singleton of class but would like to lazily load an object or child object instance. First, let's look at how to lazy load an entire simple class and then look at how to lazy load a more complex object that contains a child object. First let's create a test class that will contain a person's information that we would like to lazily load.

namespace VSMLazyLoading.Common
{
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }

        public Person()
{
Console.WriteLine(String.Format("Person created on {0}", DateTime.Now));
}

        public override string ToString()
{
return String.Format("{0} {1}", FirstName, LastName);
}
}
}

Next we implement a fake repository to load our Person object.

namespace VSMLazyLoading.Common
{
public class PersonRepository
{
public Person Load(int personId)
{
Lazy<Person> lazyPerson = new Lazy<Person>();
System.Threading.Thread.Sleep(1000);
lazyPerson.Value.FirstName = "Homer";
lazyPerson.Value.LastName = "Simpson";
lazyPerson.Value.PersonId = 1;
return lazyPerson.Value;
}
}
}

We utilize the default behavior of Lazy<T> to lazily load an instance of the Person class.

Console.WriteLine(String.Format("Created lazy initialized Person instance at  {0}.", DateTime.Now));
PersonRepository personRep = new PersonRepository();
Person person = personRep.Load(1);
Console.WriteLine(String.Format("Person: {0}", person));
Console.ReadLine();

[Click on image for larger view.]
Figure 2. The Person class.

As you can see in Figure 2, an instance of the Person class is only created at the point at which the Value property of the  Lazy<Person> instance is accessed. The Thread.Sleep call may be removed, and was only added to emphasize the point that the person object instantiation is being deferred until the Value property is set.

Complex Object Lazy Loading

Now let's look at how to defer only loading of an entire complex object. Our complex object is an Employee record that contains an associated address record. We want to lazily load both the person as well its child address record.

First let's create our Address class.

  namespace VSMLazyLoading
{
public class Address
{
public int AddressId { get; set; }
public int PersonId { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }

        public Address()
{
Console.WriteLine(String.Format("Address created on {0}.", DateTime.Now));
}

        public override string ToString()
{
return AddressLine1 + Environment.NewLine + AddressLine2 + Environment.NewLine
+ City + " " + State + ", "
+ ZipCode + Environment.NewLine + Country;
}
}

Next we'll create our Employee class, which derives from our already created Person class.
namespace VSMLazyLoading.Common
{
public class Employee : Person
{
private Lazy<Address> m_address;

        public Lazy<Address> LazyAddress
{
set
{
m_address = value;
}
}

        public Address MyAddress
{
get
{
return m_address.Value;
}
}
}

Next we create our fake AddressRepository class that will load an Address record for a given personId.

 namespace VSMLazyLoading.Common
{
public class AddressRepository
{
public Address LoadForPerson(int personId)
{
Address address = new Address()
{
AddressId = 1,
PersonId = personId,
AddressLine1 = "1234 Fake Street",
AddressLine2 = "Sector 7G",
State = "IL",
ZipCode = "60652",
Country = "USA"
};

            return address;
}
}

Now we create our fake EmployeeRepository class, which will lazily load an employee by their personId. We also lazily load the employee's address record.

namespace VSMLazyLoading.Common
{
public class EmployeeRepository
{
public Lazy<Employee> Load(int personId)
{
AddressRepository addressRep = new AddressRepository();
Lazy<Employee> lazyEmployee =
new Lazy<Employee>(() => new Employee()
{
PersonId = personId,
FirstName = "Eric",
LastName = "Vogel",
LazyAddress =
new Lazy<Address>(() => addressRep.LoadForPerson(personId))
});

            return lazyEmployee;
}
}

Finally we utilize our EmployeeRepository to lazily load our fake Employee record.

EmployeeRepository employeeRep = new EmployeeRepository();
Lazy<Employee> lazyEmployee = employeeRep.Load(1);
System.Threading.Thread.Sleep(1000);
Employee employee = lazyEmployee.Value;
Console.WriteLine(String.Format("Employee info{0} {1} {2}", Environment.NewLine, employee.FirstName, employee.LastName));
System.Threading.Thread.Sleep(1000);
Address employeeAddress = employee.MyAddress;
Console.WriteLine(String.Format("Address info:{0}{1}",Environment.NewLine, employeeAddress));
Console.ReadLine();

[Click on image for larger view.]
Figure 3. Lazily loading the Employee record.

As you can see in Figure 3, our Employee record has successfully been lazily loaded as well as its associated Address record.

Get Lazy
As you can see lazy initialization and lazy loading are two very useful programming patterns. With the introduction of the Lazy<T> implementation in the fourth iteration of the .NET Framework, utilization of these programming patterns has become much more widely accessible. As with all programming patterns, lazy initialization and loading should be used judiciously.

About the Author

Eric Vogel is a Senior Software Developer for Red Cedar Solutions Group in Okemos, Michigan. He is the president of the Greater Lansing User Group for .NET. Eric enjoys learning about software architecture and craftsmanship, and is always looking for ways to create more robust and testable applications. Contact him at vogelvision@gmail.com.

comments powered by Disqus

Featured

  • What's New in Visual Studio 2019 v16.5 Preview 2

    The second preview of Visual Studio 2019 v16.5 has arrived with improvements across the flagship IDE, including the core experience and different development areas such as C++, Python, web, mobile and so on.

  • C# Shows Strong in Tech Skills Reports

    Microsoft's C# programming language continues to show strong in tech industry skills reports, with the most recent examples coming from a skills testing company and a training company.

  • Color Shards

    Sharing Data and Splitting Components in Blazor

    ASP.NET Core Version 3.1 has at least two major changes that you'll want to take advantage of. Well, Peter thinks you will. Depending on your background, your response to one of them may be a resounding “meh.”

  • Architecture Small Graphic

    Microsoft Ships Preview SDK, Guidance for New Dual-Screen Mobile Era

    Microsoft announced a new SDK and developer guidance for dealing with the new dual-screen mobile era, ushered in by the advent of ultra-portable devices such as the Surface Duo.

  • How to Create a Machine Learning Decision Tree Classifier Using C#

    After earlier explaining how to compute disorder and split data in his exploration of machine learning decision tree classifiers, resident data scientist Dr. James McCaffrey of Microsoft Research now shows how to use the splitting and disorder code to create a working decision tree classifier.

.NET Insight

Sign up for our newsletter.

Terms and Privacy Policy consent

I agree to this site's Privacy Policy.

Upcoming Events