|
||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionTo write this article I was pushed conversation with one of the Italian software architects, namely with Giorgio Bozio. Idea with ninja is him. The topic for discussion was the question of how to use NInject for implementation Dependency Injection (DI) depending on the context. Now I suggest the decision which I use every day. Problem DomainThere are entities in our problem domain: Ninja, SuperNinja, Sword and Shuriken. On the basis of them, we will develop following classes and interfaces: interface IWeapon
{
void Use();
}
class Sword : IWeapon
{
#region IWeapon Members
public void Use()
{
Console.WriteLine("Sword was used.");
}
#endregion
}
class Shuriken : IWeapon
{
#region IWeapon Members
public void Use()
{
Console.WriteLine("Shuriken was used.");
}
#endregion
}
class Ninja
{
[Dependency]
public IWeapon Weapon { get; set; }
}
class SuperNinja
{
[Dependency]
public IWeapon SuperWeapon { get; set; }
}
I'll give some comments to source code. The weapon classes implement common IWeapon interface, but Dependency InjectionNow we will consider application DI. For this we will use one of the many frameworks Unity Application Block version 1.2. It’s non-essential, it’s possible to use both StructureMap and Castle Windsor. Simply Giorgio Bozio had problems with The code looks as follows: class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IWeapon, Sword>();
var ninja = container.Resolve<Ninja>();
ninja.Weapon.Use();
var superNinja = container.Resolve<SuperNinja>();
superNinja.SuperWeapon.Use();
Console.ReadKey();
}
}
In the beginning the container of dependences is created, and dependences are specified. Here it is obviously set that objects will receive The code output will be such:
Let's explain result. Vulnerable Contextual Dependency InjectionThere is one problem: There are two most simple ways to make this work. The first way is creation of the named dependence. The code takes the following form: class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IWeapon, Sword>();
container.RegisterType<IWeapon, Shuriken>(typeof(SuperNinja).ToString());
var ninja = container.Resolve<Ninja>();
ninja.Weapon.Use();
var superNinja = new SuperNinja();
superNinja.SuperWeapon =
container.Resolve<Shuriken>(typeof(SuperNinja).ToString());
superNinja.SuperWeapon.Use();
Console.ReadKey();
}
}
Note we have set the second dependence for the same interface. This dependence is named. A name it is possible to choose any, but we will choose it with sense - Disadvantage of this way that it is necessary to remember always that the named injection is used and don't forget to point a name at using. It is possible to overcome this disadvantage as follows: always to use the called injections. Other disadvantage is that creation of object and injection instructions has become complicated. The decision can be same, as well as at the previous disadvantage. That result which we also achieved:
The second way is creation of the nested container. The code: class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IWeapon, Sword>();
var childContainer = container.CreateChildContainer();
childContainer.RegisterType<IWeapon, Shuriken>();
var ninja = container.Resolve<Ninja>();
ninja.Weapon.Use();
var superNinja = childContainer.Resolve<SuperNinja>();
superNinja.SuperWeapon.Use();
Console.ReadKey();
}
}
Following changes have been made: the nested container of dependences has been created and the way of setting of an injection is changed. From a code it is visible that the nested container redefines dependence of the parent container. Disadvantage of this way that it is necessary to set other container for the creation of dependences and don't forget it to use. The decision consists in moving of code of creation the nested container as it is possible more close to a place of its direct using. Let's bring a certain intermediate result. It was offered two ways of the decision of a problem with contextual DI. They have the advantages and the disadvantages. It is necessary as to underline that disadvantages can be smoothed by application of a configuration file. All dependences which are set in source code, they can be set as declarative. But there is one fundamental disadvantages – injection setting isn't always set only for We can make so, for example: var ninja = container.Resolve<Ninja>(typeof(SuperNinja).ToString());
ninja.Weapon.Use();
Apparently Protected Contextual Dependency InjectionWe need cofigurate the container by addition of new dependence: container.RegisterType<SuperNinja>(
new InjectionProperty("SuperWeapon", new Shuriken()));
Here all is simple. Particularly for We will express this question in code: class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IWeapon, Sword>();
container.RegisterType<SuperNinja>(
new InjectionProperty("SuperWeapon", new Shuriken()));
var ninja = container.Resolve<Ninja>();
ninja.Weapon.Use();
var superNinja = container.Resolve<SuperNinja>();
superNinja.SuperWeapon.Use();
var superNinja2 = container.Resolve<SuperNinja>();
Console.WriteLine(
ReferenceEquals(superNinja.SuperWeapon, superNinja2.SuperWeapon));
Console.ReadKey();
}
}
And he will answer us:
I.e. it turns out that he has all the same borrowed the weapon at the friend. Speaking to the programming language, references on The code will assume the following: class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IWeapon, Sword>();
container.RegisterType<IWeapon, Shuriken>(typeof(SuperNinja).ToString());
container.RegisterType<SuperNinja>(
new InjectionProperty(
"SuperWeapon",
new ResolvedParameter<IWeapon>(typeof(SuperNinja).ToString())));
var ninja = container.Resolve<Ninja>();
ninja.Weapon.Use();
var superNinja = container.Resolve<SuperNinja>();
superNinja.SuperWeapon.Use();
var superNinja2 = container.Resolve<SuperNinja>();
Console.WriteLine(
ReferenceEquals(superNinja.SuperWeapon, superNinja2.SuperWeapon));
Console.ReadKey();
}
}
Apparently from a source code, there is a named dependence again. But one of old disadvantage, namely inconvenient creation of objects, is already irrelevant. We have reached our result:
It was possible to stop, but our code still had a number of disadvantages. We will list them: the code is depended with string name of property; Ninja class also has access to dependence of public static class UnityContainerHelper
{
private static IDictionary<Type, IUnityContainer> Container { get; set; }
public static IUnityContainer RegisterType<TFrom, TTo, TFor>(
this IUnityContainer container,
params InjectionMember[] injectionMembers)
where TTo : TFrom
{
if (Container == null)
{
Container = new Dictionary<Type, IUnityContainer>();
}
var key = typeof(TFor);
if (Container.ContainsKey(key) == false)
{
Container[key] = container.CreateChildContainer();
}
return Container[key].RegisterType<TFrom, TTo>();
}
public static T ResolveType<T>(this IUnityContainer container)
{
var key = typeof(T);
return
Container.ContainsKey(key) ?
Container[key].Resolve<T>() :
container.Resolve<T>();
}
}
We created a service class with pair extension methods. We will explain their work. The first method extends registration of dependences in The program code became simpler: class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
container.RegisterType<IWeapon, Sword>();
container.RegisterType<IWeapon, Shuriken, SuperNinja>();
var ninja = container.ResolveType<Ninja>();
ninja.Weapon.Use();
var superNinja = container.ResolveType<SuperNinja>();
superNinja.SuperWeapon.Use();
var superNinja2 = container.ResolveType<SuperNinja>();
Console.WriteLine(
ReferenceEquals(superNinja.SuperWeapon, superNinja2.SuperWeapon));
Console.ReadKey();
}
}
Note that instead of ConclusionI hope that I have answered questions Giorgio Bozio and my readers. Welcome to comments. Thank you, Oleg Smirnov.
|
|||||||||||||||||||||||||||||||||||||||||||||||