Чи може хтось пояснити Microsoft Unity?


157

Я читав статті в MSDN про Єдність (введення залежності, інверсія управління), але думаю, що мені потрібно це пояснити простими словами (або простими прикладами). Я знайомий з моделлю MVPC (ми використовуємо його тут), але я просто не можу зрозуміти цю річ Unity, і я думаю, що це наступний крок у нашій розробці додатків.


12
Мені подобається, як це має те саме ім'я, що і "Unity", тому коли я шукаю речі Unity Game Engine, я бачу цей старий технік, зітхаю. Всі добрі назви гурту взяті, я здогадуюсь.
Том Шульц

2
@ tom-schulz Old tech? nuget.org/packages/Unity - востаннє оновлено 5 днів тому.
Роджер

Відповіді:


174

Єдність - це просто контейнер IoC. Google StructureMap та спробуйте це замість цього. Я думаю, що трохи легше бавитися, коли речі IoC для вас нові.

В основному, якщо ви розумієте IoC, то ви розумієте, що те, що ви робите, - це перевернення керування, коли об’єкт створюється.

Без IoC:

public class MyClass
{
   IMyService _myService; 

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

З контейнером IoC:

public class MyClass
{
   IMyService _myService; 

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

Без IoC ваш клас, який покладається на IMyService, повинен створити конкретну версію сервісу, яку слід використовувати. І це погано з кількох причин (ви приєднали свій клас до конкретної конкретної версії IMyService, ви не можете легко перевірити його, не можете легко змінити його тощо)

За допомогою контейнера IoC ви "налаштовуєте" контейнер для вирішення цих залежностей для вас. Отже, використовуючи схему інжекцій на основі конструктора, ви просто передаєте інтерфейс залежності від IMyService в конструктор. Коли ви створите MyClass зі своїм контейнером, ваш контейнер вирішить залежність IMyService для вас.

Використовуючи StructureMap, конфігурація контейнера виглядає приблизно так:

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

Отже, те, що ви зробили, говорить контейнеру: "Коли хтось запитує IMyService, дайте їм копію SomeConcreteService." Ви також вказали, що коли хтось запитує MyClass, він отримує конкретний MyClass.

Це все, наприклад, контейнер IoC. Вони можуть зробити більше, але саме в цьому - вони вирішують залежність для вас, тому вам не доведеться (і вам не потрібно використовувати "нове" ключове слово у всьому коді).

Заключний крок: коли ви створюєте MyClass, ви зробите це:

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

Сподіваюся, що це допомагає. Не соромтеся надіслати мені електронну пошту.


2
Отже, це як фабрика, гадаю? Якщо я дотримуюся цього правильно, чи не використовуєте ви <IMyClass> замість <MyClass> в остаточному прикладі? значить, це буде var myClass = ObjectFactory.GetInstance <IMyClass> ()? Дякую за вашу допомогу, це гарний початок для мене!
Райан Абботт

3
Певним чином, це як фабрика, так. Головна фабрика для вашої програми. Але він може бути налаштований на повернення безлічі різних типів, включаючи одиночні. Що стосується інтерфейсу до MyClass - якщо це бізнес-об’єкт, я не витягав би інтерфейс. Щодо всього іншого, я взагалі хотів би.
Кріс Холмс

що робити, якщо ви тільки викликали ObjectFactory.GetInstance <MyClass> (); і ви не налаштували SomeConcreteClass? Чи отримаєте ви помилку в такому випадку?
RayLoveless

1
@Ray: Це залежить від контейнера. Деякі контейнери написані так, що за замовчуванням вони використовують умову іменування, так що якщо клас називається MyClass, а інтерфейс - ім'ям IMyInterface, контейнер автоматично налаштовує цей клас для цього інтерфейсу. Тож у такому випадку, якщо ви не налаштовуєте його вручну, все-таки "конвенція" контейнера вибирає його все одно. Однак якщо ваш клас та інтерфейс не відповідають умовам і ви не налаштовуєте контейнер для цього класу, то так, ви отримуєте помилку під час виконання.
Кріс Холмс

1
@saravanan Я думаю, що StructureMap зараз має конвенцію на основі імен. Я не впевнений; ми давно не використовували його (я написав спеціальний для нашого бізнесу; він використовує однойменну конвенцію для інтерфейсів та класів).
Кріс Холмс

39

Я щойно переглянув 30-хвилинну екранізацію IoC Screen Invicection Dependence Injection від David David Hayden і відчув, що це добре пояснення з прикладами. Ось фрагмент із приміток шоу:

На екрані показано кілька поширених звичаїв Unity IoC, таких як:

  • Створення типів не в контейнері
  • Реєстрація та вирішення типових карт
  • Реєстрація та вирішення названих типових карт
  • Singletons, LifetimeManagers і ContainerControLLifetimeManager
  • Реєстрація існуючих примірників
  • Введення залежностей у існуючі екземпляри
  • Населення UnityContainer через App.config / Web.config
  • Вказання залежностей за допомогою API введення на відміну від атрибутів залежності
  • Використання вкладених контейнерів (батьків-дитина)

32

Unity - це бібліотека, як і багато інших, яка дозволяє отримати екземпляр запитуваного типу без необхідності створювати його самостійно. Так дано.

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

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

Ви б використовували бібліотеку типу Unity для реєстрації калькулятора, який повертається, коли запитується тип ICalculator aka IoC (Інверсія управління) (цей приклад теоретичний, технічно не правильний).

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

Отже, коли ви хочете екземпляр ICalculator, ви просто ...

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

Бібліотеки IoC, як правило, можуть бути налаштовані або для утримання сингтона, або для створення нового примірника кожного разу, коли ви вирішуєте тип.

Тепер скажімо, у вас є клас, який покладається на ICalculator, щоб він був у вас.

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

    private ICalculator _calc;
}

Ви можете налаштувати бібліотеку для введення об'єкта в конструктор під час його створення.

Отже, введення DI чи залежність означає введення будь-якого іншого об'єкта, який може знадобитися.


повинен бути ICalculator calc = IoCLibrary.Resolve <ICalculator> ();
Шухрат Раймов


10

Єдність - це IoC. Сенс IoC полягає в абстрагуванні проводки залежностей між типами поза самими типами. Це має пару переваг. Перш за все, це робиться централізовано, це означає, що вам не доведеться міняти багато коду при зміні залежностей (що може стосуватися одиничних тестів).

Крім того, якщо електропроводка проводиться з використанням даних конфігурації замість коду, ви можете фактично перемотувати залежності після розгортання і таким чином змінити поведінку програми, не змінюючи код.


5

MSDN має Посібник для розробників щодо введення залежностей за допомогою Unity, який може бути корисним.

Посібник для розробників починається з основ того, що таке ін'єкція в залежність, і продовжується із прикладів того, як використовувати Unity для введення залежності. Станом на лютий 2014 року Посібник для розробників охоплює Unity 3.0, який вийшов у квітні 2013 року.


1

Я висвітлюю більшість прикладів введення залежностей у 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";
}

У DIAutoV2Controller.cs використовується механізм автоматичного впорскування

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

У DIV2Controller.cs все буде вставлено з класу Resolver Configuration Configuration

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

Налаштування вирішення залежності

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

Це не дуже корисна відповідь з ряду причин. Це надмірно складний приклад, який має занадто багато коду, щоб бути корисним, пропонуючи просте пояснення МОК. Крім того, код не чітко зафіксований у місцях, де він насправді вам потрібен.
Ден Аткінсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.