Translate

Wednesday 22 April 2009

StructureMap and Dependency Injection

StructureMap is a new library that provides for a container to manage the lifetime of an object. Because the container is responsible for instantiating an object, it has to know the types that exist in a class/interface.

The StructureMap library provides for an ObjectFactory and a Container class that helps in initialising (extracting an instance) types like interfaces and other non-instantiable types.


Object Factory
The below code, for example, initializes the container and tells it that the default concrete type (which should be returned by the container to the caller) for a request for MyInterface type is "HelloWorldClass".


ObjectFactory.Initialize(x =>{x.ForRequestedType ()
.TheDefaultIsConcreteType(); });



The Lambda Expression, "x =>", helps in providing for inline execution logic. The "ObjectFactory" container, in the above code snippet, is initialized with knowledge on "RequestedType" and "ConcreteType".


In the below code snippet, "var" type variable is used to initialize types and set the default type as in the previous code snippet. The second statement, below, injects the property with the SetProperty method and using the "target" lambda expression specifies the value for the property.


var container = new Container(r =>{
r.ForRequestedType().TheDefault.Is.OfConcreteType(); r.ForConcreteType().Configure.SetProperty(target => { target.Hello = "Hello World"; });
});


The complete code is at the end of this post. You will require StructureMap library that can be downloaded at: http://sourceforge.net/project/showfiles.php?group_id=104740


The StructureMap architecture is as below:





Source: Sourceforge.net

Dependency Injection(DI)

DI is a happening subject doing the rounds and it has entered the .Net domain, too, with a bang.

Use DI to isolate one class from the actual implementation of its dependencies.

"Depend upon Abstractions. Do not depend upon concretions"

DI happens when the creation of a type is told to it through either parameters passed to constructor (Constructor Injection) or property (Setter Injection).

The below code is based on both Constructor Injection and Setter Injection. I have used the TDD approach to write the example, which also includes a mock object (Rhino Mock). You will need NUnit, StructureMap, Rhino Mock, .Net 3.5 to execute the below code.

Commented lines are different scenarios, which you can uncomment and explore.


using NUnit.Framework;
using Rhino.Mocks;
using StructureMap;
namespace StructureMapSimple
{
public class Proxy
{
private MyInterface obj;
public Proxy(MyInterface ob)
{
obj = ob;
}
public Proxy() { }
public string CallHelloMethod()
{
return obj.SayHello();
}
}
public interface MyInterface
{
string SayHello();
}
public class HelloWorldClass : StructureMapSimple.MyInterface
{
private string strHello;
public HelloWorldClass(string helloString)
{
strHello = helloString;
}
string MyInterface.SayHello()
{
return strHello;
}
public string Hello
{
get { return strHello; }
set { strHello = value; }
}
}
class Program
{
static void Main(string[] args)
{
}
}
[TestFixture]
public class TestClass{
private Proxy ob1;
[SetUp]
public void init()
{
//ObjectFactory.Initialize(x =>
//{
// x.ForRequestedType ()

// .TheDefaultIsConcreteType();
//});
ob1 = new Proxy();
}
[Test]
public void TestSetterInjection()
{
var container = new Container(r =>
{
r.ForRequestedType().TheDefault.Is
.OfConcreteType();
r.ForConcreteType().Configure
.SetProperty(target => { target.Hello = "Hello World"; });
});
Assert.AreEqual(container.GetInstance().Hello, "Hello");
}
[Test]
public void TestConstructorInjection()
{
var container1 = new Container(r =>
{
r.ForRequestedType().TheDefault.Is
.OfConcreteType();
r.ForConcreteType().Configure
.WithCtorArg("helloString").EqualTo("Hello");
});
Assert.AreEqual(container1.GetInstance().Hello, "Hello");
}
[Test]
[Explicit]
// [ExpectedException("System.InvalidOperationException")]
public void ShouldSayHelloWithRhinoMocks()
{
MockRepository mocks = new MockRepository();
MyInterface store = (MyInterface)mocks.StrictMock(typeof MyInterface));

// is equal to "(MyInterface) StructureMap.ObjectFactory.GetInstance(typeof(MyInterface)); "
ob1 = new Proxy(store);
MyInterface ob2 = (MyInterface)StructureMap.ObjectFactory.GetInstance (typeof(MyInterface));
//Expect.Call(ob1.CallHelloMethod()).Return("Hello World");
Expect.Call(ob2.SayHello()).Return("Hello World");
mocks.ReplayAll();
string s = ob1.CallHelloMethod();
//StructureMap.ObjectFactory.Inject(typeof(MyInterface), store);
Assert.AreEqual("Hello World", s);
mocks.VerifyAll();
}
}
}

No comments: