Using Unity's automatic factories to lazy load expensive dependencies

Automatic factory support in Microsoft's Unity IoC container is not something new, having been introduced with Unity 2.0 back in 2010. Unfortunately, not many people seem to know about it and the blog post that I used to find out about it is no longer available. This short post explains how to use automatic factories and why they can be useful.

Let's look at automatic factories in an example to help understand what they are and why they are useful. We will build a very small project that has a performance issue. We will then modify the code and use Unity's automatic factory support to remedy the problem. You can follow along theoretically, or practically by just copying the code into a console app, referencing Unity and adding some plumbing here and there.

To begin, we configure the IoC container exactly as you would normally do, registering your components at the root of your application:

var container = new UnityContainer();

container.RegisterType<ITestService, TestService>()
         .RegisterType<IResource, ExpensiveResource>();	

Here we have two registrations - a TestService and an ExpensiveResource. Let's assume that the TestService makes use of the ExpensiveResource resource, so it is injected via the constructor. All classes and interfaces are displayed below:

public class TestService : ITestService
{
  private readonly IResource _resource;

  public TestService(IResource resource)
  {
    _resource = resource;
  }

  public void DoSomething()
  {
    _resource.DoSomething();
  }

  public void DoSomethingElse()
  {
    // NB _resource is not used in this method
    // ...
  }
}    

public class ExpensiveResource : IResource
{
  public ExpensiveResource()
  {
    Thread.Sleep(5000); //simulate expensive initialisation
  }

  public void DoSomething()
  {
    //do something
  }
}

public interface ITestService
{
  void DoSomething();
  void DoSomethingElse();
}

public interface IResource
{
  void DoSomething();
}
	

The important things to note are:

  1. ExpensiveResource is... well expensive. We have a Thread.Sleep call to slow it down and simulate a real world expensive resource such as an external web service call.

  2. Note all methods on the TestService class actually use the ExpensiveResource.

If you added the necessary plumbing code to actually execute both TestService methods, you would find, as you would expect, that both calls are delayed by 5 seconds because of the ExpensiveResource initialisation. So we have a performance problem. The DoSomethingElse method is delayed even though it does not use the ExpensiveResource.

This is where Unity's automatic factory support can help. We can leave the Unity registration code exactly as it is, but we will make a few small changes to the TestService class:

public class TestService : ITestService
{
  private readonly Func<IResource> _resourceFactory;

  public TestService(Func<IResource> resourceFactory)
  {
    _resourceFactory = resourceFactory;
  }

  public void DoSomething()
  {
    _resourceFactory().DoSomething();
  }

  public void DoSomethingElse()
  {
    // _resource is not used in this method
  }
}	

As you can see, we have changed the class to take in a Func delegate that returns our resource rather than the resource itself. This means that when the TestService class is constructed, the ExpensiveResource class is not constructed at the same time. It is only instantiated when we call the delegate from within the DoSomething method.

If you were to run the code again, this time only the DoSomething method would be delayed. The DoSomethingElse method would return immediately because it does not use the ExpensiveResource and by using the factory (the delegate), the ExpensiveResource constructor is not called in this case. Problem solved! When you try and resolve the TestService, Unity will scan the constructor and see that a Func is required. Unity is smart enough to resolve this delegate, provided that IResource is registered with the container. The creation of the delegate is automatic - hence automatic factories.

Note that in the previous code, we invoked the delegate in the DoSomething method on every call. If you are going to be making multiple calls within the same lifetime of the service, it might make more sense to ensure that the delegate is only invoked once. Depending on your requirements, controlling it through a Unity lifetime might suffice or alternatively, you can do something like this:

public class TestService : ITestService
{
  private IResource _resource;
  private readonly Func<IResource> _resourceFactory;

  public TestService(Func<IResource> resourceFactory)
  {
    _resourceFactory = resourceFactory;
  }

  public void DoSomething()
  {
    Resource.DoSomething();
  }

  public void DoSomethingElse()
  {
    // _resource is not used in this method
  }

  private IResource Resource
  {
    get { return _resource ?? (_resource = _resourceFactory()); }
  }
}

Now once the delegate has been invoked and the IResource implementation returned, we store it in a private member variable for future use, which is far more efficient that calling the delegate multiple times

Conclusion

Unity's automatic factory support allows you to lazy load expensive dependencies very easily. This is very useful in situations where an expensive dependency is not used in all code paths. With standard dependency injection of the resource, all methods and all code paths would experience the delays associated with instantiating the expensive resource. By injecting a factory instead, we can restrict this delay to code paths where the resource is actually used, potentially improving application performance significantly.

Useful or Interesting?

If you liked the article, I would really appreciate it if you could share it with your Twitter followers.

Share on Twitter

Comments

Avatar for Avi Jassra Avi Jassra wrote on 12/04/2013

really nice post ...

Avatar for Brett Brett wrote on 28/04/2013

Agreed! Very nice post and exactly what I was looking for.

Thanks!

Comments are now closed for this article.