DI and IoC for Beginners, Part 2
In continuation of the theme of DI / IoC, we look at the complex examples of use Unity in nontrivial scripts configuration objects.
Once again, let's write a mega-complex service:
As you might have guessed, DI will automatically work only for reference types. The code below will not work:
Once again, let's write a mega-complex service:
public class MyService
{
[Dependency]
public Random MyRandom { get; set; }
}
⋮
var uc = new UnityContainer();
var svc = uc.Resolve();
Console.WriteLine(svc.MyRandom.Next());
An attempt to create an object of type Random
via DI will fail because it Random
has several constructors and Unity, despite the obviousness that it would be necessary to call empty (i.e. new Random()
) it does not, but tries to call the most "sophisticated" one and fails. How to fix it? Something like this:// заставляем собирать Random с дефолтным конструктором
uc.RegisterType(new InjectionConstructor());
And if it Random
were our class, we could also write like this:class Random
{
[InjectionConstructor]
Random() { }
⋮
}
We just hinted to Unity that we need to use the "empty" constructor. Well, let's try the new functionality:var svc = uc.Resolve();
var svc2 = uc.Resolve();
Console.WriteLine(
ReferenceEquals(svc.MyRandom, svc2.MyRandom));
The value will be written to the console False
since The default behavior of the container is to create a new object each time. If you think that one Park and Millerovsky Random
in principle should be enough for the whole project, then how to get singleton? Yes, it’s very simple:uc.RegisterType(
new ContainerControlledLifetimeManager(),
new InjectionConstructor());
var svc = uc.Resolve();
var svc2 = uc.Resolve();
Console.WriteLine(
ReferenceEquals(svc.MyRandom, svc2.MyRandom));
This piece of code will already be written to the console True
. The parameter inheriting from LifetimeManager
determines how long the object will live. It is useful to inherit from it if you want, for example, so that for each thread / session / call a new object is issued. As you might have guessed, DI will automatically work only for reference types. The code below will not work:
public interface IService
{
int GetN();
}
public class MyService : IService
{
public int n;
public MyService(int n)
{
this.n = n;
}
public int GetN() { return n; }
}
⋮
var uc = new UnityContainer();
uc.RegisterType();
uc.RegisterType(new InjectionConstructor(10));
var svc = uc.Resolve();
Console.Write(svc.GetN());
Unfortunately, System.Int32
there was no constructor, and therefore this code, which by the way is compiled without problems, will not work. In fact, we just chose the wrong attribute - instead of manipulating the creation Int32
, in this case we need to control the creation IService
:uc.RegisterType(
new InjectionConstructor(
new InjectionParameter(10)));
All these were quite obvious manipulations of designers, let's look at an example more complicated. Suppose you have two services, and both implement IService
:public interface IService
{
void DoSomething();
}
public class MyService : IService
{
public void DoSomething()
{
Console.WriteLine("My service");
}
}
public class OtherService : IService
{
public void DoSomething()
{
Console.WriteLine("Other service");
}
}
Now create a class that consumes these services:public class Consumer
{
IService[] services;
public Consumer(IService[] services)
{
this.services = services;
}
public void DoEverything()
{
foreach (var s in services)
s.DoSomething();
}
}
An attempt to enforce Consumer
and invoke DoEverything
will not lead to anything - Unity has no idea what it would be nice to enforce IService
as an extract of all registered types IService
, and therefore it will pass it to the constructor new IService[0]
. The container again has to help:uc.RegisterType(new InjectionConstructor(
new ResolvedParameter()));
That's all for now. To be continued!