J'ai lu les articles sur MSDN sur Unity (Injection de dépendance, Inversion de contrôle), mais je pense avoir besoin de l'expliquer en termes simples (ou d'exemples simples). Je suis familier avec le modèle MVPC (nous l'utilisons ici), mais je ne parviens pas encore à saisir ce problème d'Unity, et je pense que c'est la prochaine étape dans la conception de notre application.
L'unité est juste un "conteneur" d'IoC. Google StructureMap et essayez-le à la place. Je pense que c'est un peu plus facile à maîtriser lorsque le contenu de l'IoC est nouveau pour vous.
En gros, si vous comprenez IoC, vous comprenez que ce que vous faites, c'est d'inverser le contrôle pour la création d'un objet.
Sans IoC:
public class MyClass
{
IMyService _myService;
public MyClass()
{
_myService = new SomeConcreteService();
}
}
Avec conteneur IoC:
public class MyClass
{
IMyService _myService;
public MyClass(IMyService myService)
{
_myService = myService;
}
}
Sans IoC, votre classe qui s'appuie sur IMyService doit créer une version concrète du service à utiliser. Et c’est mauvais pour un certain nombre de raisons (vous avez associé votre classe à une version concrète spécifique d’IMyService, vous ne pouvez pas le tester à l'unité facilement, vous ne pouvez pas le changer facilement, etc.)
Avec un conteneur IoC, vous "configurez" le conteneur pour résoudre ces dépendances à votre place. Donc, avec un schéma d'injection basé sur le constructeur, vous passez simplement l'interface à la dépendance IMyService dans le constructeur. Lorsque vous créez la classe MyClass avec votre conteneur, celui-ci résout pour vous la dépendance IMyService.
Avec StructureMap, la configuration du conteneur ressemble à ceci:
StructureMapConfiguration.ForRequestedType<MyClass>().TheDefaultIsConcreteType<MyClass>();
StructureMapConfiguration.ForRequestedType<IMyService>().TheDefaultIsConcreteType<SomeConcreteService>();
Donc, ce que vous avez fait est dit au conteneur: "Quand quelqu'un demande le service IMyService, donnez-lui une copie du SomeConcreteService." Et vous avez également précisé que lorsque quelqu'un demande une MyClass, il reçoit une MyClass concrète.
C'est tout ce qu'un conteneur IoC fait vraiment. Ils peuvent faire plus, mais c'est le sens du problème: ils résolvent les dépendances pour vous, de sorte que vous n'avez pas à le faire (et que vous n'avez pas à utiliser le "nouveau" mot-clé dans tout votre code).
Dernière étape: lorsque vous créez votre MyClass, procédez comme suit:
var myClass = ObjectFactory.GetInstance<MyClass>();
J'espère que ça t'as aidé. N'hésitez pas à m'envoyer un mail.
Je viens de regarder le screencast IoC Injection Dependency Injection de 30 minutes de David Hayden et j’ai trouvé que c’était une bonne explication avec des exemples. Voici un extrait des notes de l'émission:
Le screencast montre plusieurs utilisations courantes de Unity IoC, telles que:
Unity est une bibliothèque comme beaucoup d’autres qui vous permet d’obtenir une instance du type demandé sans avoir à la créer vous-même. Donc donné.
public interface ICalculator
{
void Add(int a, int b);
}
public class Calculator : ICalculator
{
public void Add(int a, int b)
{
return a + b;
}
}
Vous utiliseriez une bibliothèque telle que Unity pour enregistrer Calculator à renvoyer lorsque le type ICalculator est demandé, à savoir IoC (Inversion of Control) (cet exemple est théorique, pas techniquement correct).
IoCLlibrary.Register<ICalculator>.Return<Calculator>();
Alors maintenant, quand vous voulez une instance d'un ICalculator, vous venez de ...
Calculator calc = IoCLibrary.Resolve<ICalculator>();
Les bibliothèques IoC peuvent généralement être configurées pour contenir un singleton ou créer une nouvelle instance chaque fois que vous résolvez un type.
Maintenant, supposons que vous ayez une classe qui repose sur un calculateur IC que vous pourriez avoir ..
public class BankingSystem
{
public BankingSystem(ICalculator calc)
{
_calc = calc;
}
private ICalculator _calc;
}
Et vous pouvez configurer la bibliothèque pour injecter un objet dans le constructeur lors de sa création.
Ainsi, DI ou Dependency Injection signifie injecter tout objet pouvant être requis par un autre utilisateur.
Ce mec WilcoxTutorials donne une excellente démonstration du conteneur Unity destiné aux débutants.
Partie 1: http://www.youtube.com/watch?v=CWwe9Z0Gyew
Partie 2: http://www.youtube.com/watch?v=PsIbevgzQQE
En moins d'une demi-heure et vous comprendrez les bases!
L'unité est un IoC. Le but d'IoC est de résumer le câblage des dépendances entre les types extérieurs aux types eux-mêmes. Cela présente quelques avantages. Tout d'abord, cela se fait de manière centralisée, ce qui signifie que vous n'avez pas à changer beaucoup de code lorsque les dépendances changent (ce qui peut être le cas pour les tests unitaires).
De plus, si le câblage est effectué à l'aide de données de configuration plutôt que de code, vous pouvez réellement reconnecter les dépendances après le déploiement et modifier ainsi le comportement de l'application sans modifier le code.
MSDN a un Guide du développeur pour l’injection de dépendances sous Unity qui peut être utile.
Le Guide du développeur commence par les bases de la définition de l'injection de dépendance et se poursuit par des exemples d'utilisation de Unity pour l'injection de dépendance. Depuis février 2014, le Guide du développeur couvre Unity 3.0, publié en avril 2013.
Je couvre la plupart des exemples de Dependency Injection dans ASP.NET Web API 2
public interface IShape
{
string Name { get; set; }
}
public class NoShape : IShape
{
public string Name { get; set; } = "I have No Shape";
}
public class Circle : IShape
{
public string Name { get; set; } = "Circle";
}
public class Rectangle : IShape
{
public Rectangle(string name)
{
this.Name = name;
}
public string Name { get; set; } = "Rectangle";
}
Dans DIAutoV2Controller.cs, le mécanisme d’injection automatique est utilisé
[RoutePrefix("api/v2/DIAutoExample")]
public class DIAutoV2Controller : ApiController
{
private string ConstructorInjected;
private string MethodInjected1;
private string MethodInjected2;
private string MethodInjected3;
[Dependency]
public IShape NoShape { get; set; }
[Dependency("Circle")]
public IShape ShapeCircle { get; set; }
[Dependency("Rectangle")]
public IShape ShapeRectangle { get; set; }
[Dependency("PiValueExample1")]
public double PiValue { get; set; }
[InjectionConstructor]
public DIAutoV2Controller([Dependency("Circle")]IShape shape1, [Dependency("Rectangle")]IShape shape2, IShape shape3)
{
this.ConstructorInjected = shape1.Name + " & " + shape2.Name + " & " + shape3.Name;
}
[NonAction]
[InjectionMethod]
public void Initialize()
{
this.MethodInjected1 = "Default Initialize done";
}
[NonAction]
[InjectionMethod]
public void Initialize2([Dependency("Circle")]IShape shape1)
{
this.MethodInjected2 = shape1.Name;
}
[NonAction]
[InjectionMethod]
public void Initialize3(IShape shape1)
{
this.MethodInjected3 = shape1.Name;
}
[HttpGet]
[Route("constructorinjection")]
public string constructorinjection()
{
return "Constructor Injected: " + this.ConstructorInjected;
}
[HttpGet]
[Route("GetNoShape")]
public string GetNoShape()
{
return "Property Injected: " + this.NoShape.Name;
}
[HttpGet]
[Route("GetShapeCircle")]
public string GetShapeCircle()
{
return "Property Injected: " + this.ShapeCircle.Name;
}
[HttpGet]
[Route("GetShapeRectangle")]
public string GetShapeRectangle()
{
return "Property Injected: " + this.ShapeRectangle.Name;
}
[HttpGet]
[Route("GetPiValue")]
public string GetPiValue()
{
return "Property Injected: " + this.PiValue;
}
[HttpGet]
[Route("MethodInjected1")]
public string InjectionMethod1()
{
return "Method Injected: " + this.MethodInjected1;
}
[HttpGet]
[Route("MethodInjected2")]
public string InjectionMethod2()
{
return "Method Injected: " + this.MethodInjected2;
}
[HttpGet]
[Route("MethodInjected3")]
public string InjectionMethod3()
{
return "Method Injected: " + this.MethodInjected3;
}
}
Dans DIV2Controller.cs, tout sera injecté à partir de la classe Resolver Configuration Resolver
[RoutePrefix("api/v2/DIExample")]
public class DIV2Controller : ApiController
{
private string ConstructorInjected;
private string MethodInjected1;
private string MethodInjected2;
public string MyPropertyName { get; set; }
public double PiValue1 { get; set; }
public double PiValue2 { get; set; }
public IShape Shape { get; set; }
// MethodInjected
[NonAction]
public void Initialize()
{
this.MethodInjected1 = "Default Initialize done";
}
// MethodInjected
[NonAction]
public void Initialize2(string myproperty1, IShape shape1, string myproperty2, IShape shape2)
{
this.MethodInjected2 = myproperty1 + " & " + shape1.Name + " & " + myproperty2 + " & " + shape2.Name;
}
public DIV2Controller(string myproperty1, IShape shape1, string myproperty2, IShape shape2)
{
this.ConstructorInjected = myproperty1 + " & " + shape1.Name + " & " + myproperty2 + " & " + shape2.Name;
}
[HttpGet]
[Route("constructorinjection")]
public string constructorinjection()
{
return "Constructor Injected: " + this.ConstructorInjected;
}
[HttpGet]
[Route("PropertyInjected")]
public string InjectionProperty()
{
return "Property Injected: " + this.MyPropertyName;
}
[HttpGet]
[Route("GetPiValue1")]
public string GetPiValue1()
{
return "Property Injected: " + this.PiValue1;
}
[HttpGet]
[Route("GetPiValue2")]
public string GetPiValue2()
{
return "Property Injected: " + this.PiValue2;
}
[HttpGet]
[Route("GetShape")]
public string GetShape()
{
return "Property Injected: " + this.Shape.Name;
}
[HttpGet]
[Route("MethodInjected1")]
public string InjectionMethod1()
{
return "Method Injected: " + this.MethodInjected1;
}
[HttpGet]
[Route("MethodInjected2")]
public string InjectionMethod2()
{
return "Method Injected: " + this.MethodInjected2;
}
}
Configuration du résolveur de dépendance
public static void Register(HttpConfiguration config)
{
var container = new UnityContainer();
RegisterInterfaces(container);
config.DependencyResolver = new UnityResolver(container);
// Other Web API configuration not shown.
}
private static void RegisterInterfaces(UnityContainer container)
{
var dbContext = new SchoolDbContext();
// Registration with constructor injection
container.RegisterType<IStudentRepository, StudentRepository>(new InjectionConstructor(dbContext));
container.RegisterType<ICourseRepository, CourseRepository>(new InjectionConstructor(dbContext));
// Set constant/default value of Pi = 3.141
container.RegisterInstance<double>("PiValueExample1", 3.141);
container.RegisterInstance<double>("PiValueExample2", 3.14);
// without a name
container.RegisterInstance<IShape>(new NoShape());
// with circle name
container.RegisterType<IShape, Circle>("Circle", new InjectionProperty("Name", "I am Circle"));
// with rectangle name
container.RegisterType<IShape, Rectangle>("Rectangle", new InjectionConstructor("I am Rectangle"));
// Complex type like Constructor, Property and method injection
container.RegisterType<DIV2Controller, DIV2Controller>(
new InjectionConstructor("Constructor Value1", container.Resolve<IShape>("Circle"), "Constructor Value2", container.Resolve<IShape>()),
new InjectionMethod("Initialize"),
new InjectionMethod("Initialize2", "Value1", container.Resolve<IShape>("Circle"), "Value2", container.Resolve<IShape>()),
new InjectionProperty("MyPropertyName", "Property Value"),
new InjectionProperty("PiValue1", container.Resolve<double>("PiValueExample1")),
new InjectionProperty("Shape", container.Resolve<IShape>("Rectangle")),
new InjectionProperty("PiValue2", container.Resolve<double>("PiValueExample2")));
}