Introduction
In this tip, I am going to show you how to instantiate
object on demand using System.Lazy & Windsor Castle (DI Framework).
Background
In an application which I was working on,
I came across an issue where developers have used lots of interfaces to
instantiate a class from a single constructor. Application was been developed
using Windsor DI framework. After doing some Googling, I came to know that this
Windsor instantiates using constructor which has minimum number of parameters
and we can’t control the constructor which needs to be instantiated. Here I see a problem. What if one of the instances
goes down with some reason and this instance does have any role to play for
completing the process.
Changing implementation at this point of
time will be a big cost to pay. Other impact would be the Unit Test which has already been developed needs to changed.
Using the Code
Add a reference to Castle Windsor using Visual Studio Nuget. This
will setup castle reference in your project. Include the following namespace in your class file.
using Castle.MicroKernel.Resolvers;
using Castle.Windsor;
For activating lazy loading in Windsor,
you need to register interface ILazyComponentLoader with class LazyOfTComponentLoader.
var container = new WindsorContainer();
container.Register(
Castle.MicroKernel.Registration.Component.For
<ILazyComponentLoader>().ImplementedBy<LazyOfTComponentLoader>(),
Castle.MicroKernel.Registration.Component.For
<ITestA>().ImplementedBy<TestA>().LifestyleTransient(),
Castle.MicroKernel.Registration.Component.For
<ITestB>().ImplementedBy<TestB>().LifestyleTransient(),
Castle.MicroKernel.Registration.Component.For
<ITestLazyloading>().ImplementedBy<TestLazyloading>());
Now, the other change which you need to make
with your class constructor is start taking this parameter via System.Lazy
. You
only need to make this change if you want your parameter to be Lazy loaded.
public TestLazyloading(Lazy<ITestA> testA, Lazy<ITestB> testB)
Now you will have to use LazyObject.Value
to activate & access its
attributes.
AObject.Value.DoWork();
This will do the trick. Once you access .Value
object would get instantiated.
Since you have made a change to your
class constructor, you will also have to change your unit test. Making change to
your unit test class would be simple. Create object of System.Lazy<T>
and
return the mock class which you were referring earlier.
var lazyTestA = new Lazy<ITestA>(() => new TestA());
Set
the object you have created in your class constructor for testing.
That’s it. We are done with the changes we have to with our existing code.
You can find the below sample code using
console application.
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.Register(
Castle.MicroKernel.Registration.Component.For
<ILazyComponentLoader>().ImplementedBy<LazyOfTComponentLoader>(),
Castle.MicroKernel.Registration.Component.For
<ITestA>().ImplementedBy<TestA>().LifestyleTransient(),
Castle.MicroKernel.Registration.Component.For
<ITestB>().ImplementedBy<TestB>().LifestyleTransient(),
Castle.MicroKernel.Registration.Component.For
<ITestLazyloading>().ImplementedBy<TestLazyloading>()
);
Console.WriteLine("Lazy loading execution Start");
var test = container.Resolve<Lazy<ITestLazyloading>>();
test.Value.TestingInstanceA();
Console.WriteLine("Lazy loading execution complete");
Console.ReadKey();
Console.WriteLine("Executing Unit test case scenario start");
var lazyTestA = new Lazy<ITestA>(() => new TestA());
var lazyTestB = new Lazy<ITestB>(() => new TestB());
var test2 = new TestLazyloading(lazyTestA, lazyTestB);
test2.TestingInstanceA();
test2.TestingInstanceB();
Console.WriteLine("Executing Unit test case scenario end");
Console.ReadKey();
}
}
public interface ITestA
{
void DoWork();
}
public interface ITestB
{
void DoMyWork();
}
public class TestA : ITestA
{
public TestA()
{
Console.WriteLine("Test A Activated");
}
public void DoWork()
{
}
}
public class TestB : ITestB
{
public TestB()
{
Console.WriteLine("Test B Activated");
}
public void DoMyWork()
{
}
}
public interface ITestLazyloading
{
void TestingInstanceA();
void TestingInstanceB();
}
public class TestLazyloading : ITestLazyloading
{
private Lazy<ITestA> AObject = null;
private Lazy<ITestB> BOject = null;
public TestLazyloading(Lazy<ITestA> testA, Lazy<ITestB> testB)
{
AObject = testA;
BOject = testB;
}
public void TestingInstanceA()
{
AObject.Value.DoWork();
Console.WriteLine("A is been executed");
}
public void TestingInstanceB()
{
BOject.Value.DoMyWork();
Console.WriteLine("B is been executed");
}
}
Console Application Output
Lazy loading execution Start
Test A Activated
A is been executed
Lazy loading execution complete
Executing Unit test case scenario start
Test A Activated
A is been executed
Test B Activated
B is been executed
Executing Unit test case scenario end