Poate cineva să explice Microsoft Unity? (Programare, C#, Injecție De Dependență, Inversiune De Control, Container Unitate)

Ryan Abbott a intrebat.

Am citit articolele de pe MSDN despre Unity (Dependency Injection, Inversion of Control), dar cred că am nevoie de o explicație în termeni simpli (sau exemple simple). Sunt familiarizat cu modelul MVPC (îl folosim aici), dar încă nu reușesc să înțeleg cu adevărat acest lucru Unity și cred că este următorul pas în proiectarea aplicației noastre.

Comentarii

  • Îmi place că are același nume ca și „Unity”, așa că atunci când caut chestii despre Unity Game Engine văd această tehnologie veche, sigh. Toate numele de trupe bune sunt luate, cred. –  > Por Tom Schulz.
  • @tom-schulz Tehnologie veche? nuget.org/packages/Unity – ultima actualizare acum 5 zile. –  > Por Roger Willcocks.
6 răspunsuri
Chris Holmes

Unity este doar un „container” IoC. Google StructureMap și încercați-l în schimb. Un pic mai ușor de înțeles, cred, atunci când chestiile IoC sunt noi pentru tine.

Practic, dacă înțelegeți IoC, atunci înțelegeți că ceea ce faceți este să inversați controlul pentru momentul în care un obiect este creat.

Fără IoC:

public class MyClass
{
   IMyService _myService; 

   public MyClass()
   {
      _myService = new SomeConcreteService();    
   }
}

Cu container IoC:

public class MyClass
{
   IMyService _myService; 

   public MyClass(IMyService myService)
   {
      _myService = myService;    
   }
}

Fără IoC, clasa dvs. care se bazează pe IMyService trebuie să creeze o versiune concretă a serviciului pentru a-l utiliza. Iar acest lucru este rău din mai multe motive (v-ați cuplat clasa la o anumită versiune concretă a IMyService, nu o puteți testa cu ușurință, nu o puteți modifica cu ușurință etc.).

Cu un container IoC, „configurați” containerul pentru a rezolva aceste dependențe pentru dumneavoastră. Astfel, cu o schemă de injecție bazată pe constructor, trebuie doar să treceți interfața dependenței IMyService în constructor. Când creați MyClass cu containerul dvs., containerul va rezolva dependența IMyService pentru dvs.

Folosind StructureMap, configurarea containerului arată astfel:

StructureMapConfiguration.ForRequestedType<MyClass>().TheDefaultIsConcreteType<MyClass>();
StructureMapConfiguration.ForRequestedType<IMyService>().TheDefaultIsConcreteType<SomeConcreteService>();

Așadar, ați spus containerului: „Când cineva solicită IMyService, oferiți-i o copie a SomeConcreteService”. Și ați specificat, de asemenea, că atunci când cineva solicită un MyClass, primește un MyClass concret.

Asta este tot ceea ce face cu adevărat un container IoC. Pot face mai mult, dar acesta este scopul principal – rezolvă dependențele pentru dvs., astfel încât nu trebuie să o faceți dvs. (și nu trebuie să folosiți cuvântul cheie „new” în tot codul dvs.).

Ultimul pas: când creați MyClass, faceți astfel:

var myClass = ObjectFactory.GetInstance<MyClass>();

Sper că vă ajută. Nu ezitați să îmi trimiteți un e-mail.

Comentarii

  • Deci este ca o fabrică, presupun? Dacă înțeleg corect, nu ar trebui să folosiți <IMyClass> în loc de <MyClass> în exemplul final? deci ar fi var myClass = ObjectFactory.GetInstance<IMyClass>()? Vă mulțumesc pentru ajutor, acesta este un bun început pentru mine! –  > Por Ryan Abbott.
  • Într-un fel, este ca o fabrică, da. O fabrică principală pentru aplicația dumneavoastră. Dar poate fi configurată pentru a returna o mulțime de tipuri diferite, inclusiv singletoni. În ceea ce privește interfața pentru MyClass – dacă este un obiect de afaceri, nu aș extrage o interfață. Pentru orice altceva, în general, aș face-o. –  > Por Chris Holmes.
  • ce se întâmplă dacă ați apelat doar ObjectFactory.GetInstance<MyClass>(); și nu ați configurat SomeConcreteClass? Ați obține o eroare în acest caz? –  > Por RayLoveless.
  • @Ray: Depinde de container. Unele containere sunt scrise astfel încât, în mod implicit, să utilizeze o convenție de denumire, astfel încât, dacă o clasă se numește MyClass și interfața se numește IMyInterface, containerul va configura automat clasa respectivă pentru acea interfață. Astfel, în acest caz, dacă nu o configurați manual, „convenția” implicită a containerului o preia oricum. Cu toate acestea, dacă clasa și interfața dvs. nu respectă convenția și nu configurați containerul pentru clasa respectivă, atunci da, veți primi o eroare în timpul execuției. –  > Por Chris Holmes.
  • @saravanan Cred că StructureMap face acum o convenție bazată pe nume. Nu sunt sigur; nu am mai folosit-o de mult timp (am scris una personalizată pentru afacerea noastră; folosește convenția cu același nume pentru interfețe și clase). –  > Por Chris Holmes.
Kevin Hakanson

Tocmai am vizionat Screencast-ul de 30 de minute despre Unity Dependency Injection IoC de David Hayden și am considerat că a fost o explicație bună cu exemple. Iată un fragment din notele emisiunii:

Screencastul prezintă mai multe utilizări comune ale Unity IoC, cum ar fi:

  • Crearea de tipuri care nu se află în container
  • Înregistrarea și rezolvarea tipurilor (TypeMappings)
  • Înregistrarea și rezolvarea tipurilor numite TypeMappings
  • Singletons, LifetimeManagers și ContainerControlledLifetimeManager
  • Înregistrarea instanțelor existente
  • Injectarea de dependențe în instanțe existente
  • Popularea UnityContainer prin App.config / Web.config
  • Specificarea dependențelor prin intermediul API de injectare, spre deosebire de atributele de dependență
  • Utilizarea containerelor imbricate (părinte-copil)

Chad Moran

Unity este o bibliotecă, ca multe altele, care vă permite să obțineți o instanță a unui tip solicitat fără a fi nevoie să o creați dumneavoastră. Deci, având în vedere.

public interface ICalculator
{
    void Add(int a, int b);
}

public class Calculator : ICalculator
{
    public void Add(int a, int b)
    {
        return a + b;
    }
}

Ar trebui să folosiți o bibliotecă precum Unity pentru a înregistra Calculator pentru a fi returnat atunci când este solicitat tipul ICalculator, aka IoC (Inversion of Control) (acest exemplu este teoretic, nu este corect din punct de vedere tehnic).

IoCLlibrary.Register<ICalculator>.Return<Calculator>();

Astfel, atunci când doriți o instanță a unui ICalculator, trebuie doar să…

Calculator calc = IoCLibrary.Resolve<ICalculator>();

Bibliotecile IoC pot fi configurate, de obicei, fie pentru a păstra un singleton, fie pentru a crea o nouă instanță de fiecare dată când rezolvați un tip.

Acum, să spunem că aveți o clasă care se bazează pe prezența unui ICalculator, ați putea avea…

public class BankingSystem
{
    public BankingSystem(ICalculator calc)
    {
        _calc = calc;
    }

    private ICalculator _calc;
}

Și puteți configura biblioteca pentru a injecta un obiect în constructor atunci când acesta este creat.

Așadar, DI sau Dependency Injection înseamnă injectarea oricărui obiect de care ar putea avea nevoie altcineva.

Comentarii

  • ar trebui să fie ICalculator calc = IoCLibrary.Resolve<ICalculator>(); –  > Por Shukhrat Raimov.
Brian Rasmussen

Unity este un IoC. Ideea IoC este de a abstractiza cablarea dependențelor dintre tipuri în afara tipurilor însele. Acest lucru are câteva avantaje. În primul rând, se face în mod centralizat, ceea ce înseamnă că nu trebuie să modificați o mulțime de cod atunci când se schimbă dependențele (ceea ce poate fi cazul testelor unitare).

În plus, dacă cablarea se face folosind date de configurare în loc de cod, puteți, de fapt, să refaceți cablarea dependențelor după implementare și, astfel, să schimbați comportamentul aplicației fără a modifica codul.

Simon Tewsi

MSDN are un Ghidul dezvoltatorului pentru injecția de dependență folosind Unity care ar putea fi util.

Ghidul dezvoltatorului începe cu noțiuni de bază despre ce este injecția de dependență și continuă cu exemple de utilizare a Unity pentru injecția de dependență. Începând cu februarie 2014, Ghidul dezvoltatorului acoperă Unity 3.0, care a fost lansat în aprilie 2013.

Narottam Goyal

Acopăr majoritatea exemplelor de Injectare a dependenței în 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";
}

În DIAutoV2Controller.cs se utilizează mecanismul Auto Injection

[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;
    }
}

În DIV2Controller.cs totul va fi injectat din clasa Dependency 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;
    }
}

Configurarea Resolverului de dependență

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")));
}

Comentarii

  • Acesta nu este un răspuns deosebit de util din mai multe motive. Este un exemplu inutil de complex, care conține prea mult cod pentru a fi util în oferirea unei explicații simple a IOC. În plus, codul nu este documentat în mod clar în locurile în care ați avea nevoie de el. –  > Por Dan Atkinson.