Home .NET DI and IoC for Beginners, Part 2

DI and IoC for Beginners, Part 2

by admin

In continuation of the topic on DI/IoC, we’ll look at sophisticated examplesof using Unity innon-trivial object configuration scenarios.
Let’s once again write a mega-complex service :

public class MyService<br/>
{<br/>
[Dependency]<br/>
public RandomMyRandom{ get; set; }<br/>
}<br/>
<br/>
⋮<br/>
<br/>
var uc = new UnityContainer();<br/>
var svc = uc.Resolve<MyService> ();<br/>
Console.WriteLine(svc.MyRandom.Next());<br/>

Attempting to create an object of type Random via DI will fail because the Random has multiple constructors and Unity, despite the obviousness of calling empty (i.e. newRandom() ) doesn’t do that, but tries to call the most "smart" one and fails. How to fix it? Like this:

// force Random to build with the default constructor
uc.RegisterType<Random> ( new InjectionConstructor());<br/>

And if Random was our class, you could also write it like this:

class Random<br/>
{<br/>
[InjectionConstructor]<br/>
Random() { }<br/>
⋮<br/>
}<br/>

We just hinted to Unity that we should use an "empty" constructor. Well, let’s try to use the newfunctionality :

var svc = uc.Resolve<MyService> ();<br/>
var svc2 = uc.Resolve<MyService> ();<br/>
Console.WriteLine(<br/>
ReferenceEquals(svc.MyRandom, svc2.MyRandom));<br/>

The console will write the value False because the default behavior of the container is to create a newobject every time. If you think that one Park-and-Miller Random should in principle be enough for the whole project, then how do you get a singleton? Very simple :

uc.RegisterType<Random> (<br/>
new ContainerControlledLifetimeManager(), <br/>
new InjectionConstructor());<br/>
var svc = uc.Resolve<MyService> ();<br/>
var svc2 = uc.Resolve<MyService> ();<br/>
Console.WriteLine(<br/>
ReferenceEquals(svc.MyRandom, svc2.MyRandom));<br/>

This bit of code will already write in the console True Parameter inherited from LifetimeManager Defines how long the object will live. This is useful to inherit if you want, for example, to have a newobject for each thread/session/version.
As you have probably guessed by now, the DI will automatically work for reference types only. The code below will not work:

public interface IService<br/>
{<br/>
int GetN();<br/>
}<br/>
<br/>
public class MyService :IService<br/>
{<br/>
public int n;<br/>
public MyService( int n)<br/>
{<br/>
this n = n;<br/>
}<br/>
public int GetN() { return n; }<br/>
}<br/>
⋮<br/>
var uc = new UnityContainer();<br/>
uc.RegisterType<IService, MyService> ();<br/>
uc.RegisterType< int > ( new InjectionConstructor(10));<br/>
var svc = uc.Resolve<IService> ();<br/>
Console.Write(svc.GetN());<br/>

Unfortunately, the System.Int32 didn’t find a constructor and that’s why this code, which by the way compiles fine, won’t work. Actually we just chose a wrong attribute – instead of manipulating the creation of Int32 , in this case we need to manipulate the creation of IService :

uc.RegisterType<IService, MyService> (<br/>
new InjectionConstructor(<br/>
new InjectionParameter(10)));<br/>

These were all pretty obvious constructor manipulations, let’s look at a more complicated example. Suppose you have two services, and both implement IService :

public interface IService<br/>
{<br/>
void DoSomething();<br/>
}<br/>
public class MyService : IService<br/>
{<br/>
public void DoSomething()<br/>
{<br/>
Console.WriteLine( "My service" );<br/>
}<br/>
}<br/>
public class OtherService : IService<br/>
{<br/>
public void DoSomething()<br/>
{<br/>
Console.WriteLine( "Other service" );<br/>
}<br/>
}<br/>

Now create a classthat consumes these services :

public class Consumer<br/>
{<br/>
IService[] services;<br/>
public Consumer(IService[] services)<br/>
{<br/>
this services = services;<br/>
}<br/>
public void DoEverything() <br/>
{<br/>
foreach ( var s in services)<br/>
s.DoSomething();<br/>
}<br/>
}<br/>

Attempting to block Consumer and call DoEverything will get you nowhere – Unity has no idea that it would be a good idea to call IService as an extract of all registered types IService , and so the constructor will pass new IService[0] The container again has to help :

uc.RegisterType<Consumer> ( new InjectionConstructor(<br/>
new ResolvedParameter<IService[]> ()));<br/>

That’s it for now. More to come!

You may also like