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 [email protected].

comments powered by Disqus

Featured

  • Mastering Blazor Authentication and Authorization

    At the Visual Studio Live! @ Microsoft HQ developer conference set for August, Rockford Lhotka will explain the ins and outs of authentication across Blazor Server, WebAssembly, and .NET MAUI Hybrid apps, and show how to use identity and claims to customize application behavior through fine-grained authorization.

  • Linear Support Vector Regression from Scratch Using C# with Evolutionary Training

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the linear support vector regression (linear SVR) technique, where the goal is to predict a single numeric value. A linear SVR model uses an unusual error/loss function and cannot be trained using standard simple techniques, and so evolutionary optimization training is used.

  • Low-Code Report Says AI Will Enhance, Not Replace DIY Dev Tools

    Along with replacing software developers and possibly killing humanity, advanced AI is seen by many as a death knell for the do-it-yourself, low-code/no-code tooling industry, but a new report belies that notion.

  • Vibe Coding with Latest Visual Studio Preview

    Microsoft's latest Visual Studio preview facilitates "vibe coding," where developers mainly use GitHub Copilot AI to do all the programming in accordance with spoken or typed instructions.

  • Steve Sanderson Previews AI App Dev: Small Models, Agents and a Blazor Voice Assistant

    Blazor creator Steve Sanderson presented a keynote at the recent NDC London 2025 conference where he previewed the future of .NET application development with smaller AI models and autonomous agents, along with showcasing a new Blazor voice assistant project demonstrating cutting-edge functionality.

Subscribe on YouTube