Вопрос

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

Это было полезно?

Решение

Unity - это всего лишь "контейнер" IoC.Погуглите StructureMap и попробуйте его вместо этого.Я думаю, немного легче ориентироваться, когда информация о МоК для тебя в новинку.

В принципе, если вы понимаете 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.Они могут делать больше, но в этом и суть - они разрешают зависимости за вас, так что вам не нужно (и вам не нужно использовать ключевое слово "new" во всем вашем коде).

Заключительный шаг:когда вы создадите свой MyClass, вы сделаете это:

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

Надеюсь, это поможет.Не стесняйтесь писать мне по электронной почте.

Другие советы

Я только что посмотрел 30-минутный скринкаст с внедрением зависимостей в Unity IoC Дэвида Хейдена и почувствовал, что это хорошее объяснение с примерами.Вот фрагмент из заметок к шоу:

В скринкасте показано несколько распространенных способов использования Unity IoC, таких как:

  • Создание типов Не В Контейнере
  • Регистрация и разрешение сочетаний типов
  • Регистрация и разрешение именованных приложений типов
  • Синглтоны, LifetimeManagers и ContainerControlledLifetimeManager
  • Регистрация существующих Экземпляров
  • Внедрение зависимостей в существующие экземпляры
  • Заполнение UnityContainer через App.config / Web.config
  • Указание зависимостей через API внедрения в отличие от атрибутов зависимостей
  • Использование вложенных ( Родительско-дочерних) контейнеров

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, известного как 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 или внедрение зависимостей означает внедрение любого объекта, который может потребоваться другому.

Этот парень WilcoxTutorials дает отличную демонстрацию контейнера Unity, который предназначен для новичков.

Часть 1: http://www.youtube.com/watch?v=CWwe9Z0Gyew

Часть 2: http://www.youtube.com/watch?v=PsIbevgzQQE

Менее чем за полчаса вы поймете основы!

Юнити - это МоК.Смысл IoC состоит в том, чтобы абстрагировать связывание зависимостей между типами вне самих типов.У этого есть пара преимуществ.Прежде всего, это делается централизованно, что означает, что вам не нужно менять много кода при изменении зависимостей (что может иметь место в модульных тестах).

Более того, если подключение выполняется с использованием конфигурационных данных вместо кода, вы можете фактически переподключить зависимости после развертывания и, таким образом, изменить поведение приложения без изменения кода.

MSDN имеет Руководство разработчика по внедрению зависимостей с использованием Unity это может быть полезно.

Руководство разработчика начинается с основ того, что такое внедрение зависимостей, и продолжается примерами того, как использовать Unity для внедрения зависимостей.По состоянию на февраль 2014 года Руководство разработчика охватывает Unity 3.0, которая была выпущена в апреле 2013 года.

Я рассматриваю большинство примеров внедрения зависимостей в 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 все будет введено из класса распознавателя конфигурации зависимостей

[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")));
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top