Внедрение зависимостейDependency Injection

Как правило вызывается конструктор класса, при создании экземпляра объекта, и все значения, необходимые для объекта передаются как аргументы в конструктор.Typically, a class constructor is invoked when instantiating an object, and any values that the object needs are passed as arguments to the constructor. Это может служить примером внедрения зависимостей и в частности называется внедрение через конструктор.This is an example of dependency injection, and specifically is known as constructor injection. Зависимости, необходимые на объекте добавляются в конструктор.The dependencies the object needs are injected into the constructor.

Указав зависимости как типы интерфейса, внедрение зависимостей обеспечивает разделение конкретные типы из кода, который зависит от этих типов.By specifying dependencies as interface types, dependency injection enables decoupling of the concrete types from the code that depends on these types. Обычно в нем контейнер, который содержит список регистраций и сопоставления между интерфейсы и абстрактные типы и конкретные типы, которые реализуют или расширяют эти типы.It generally uses a container that holds a list of registrations and mappings between interfaces and abstract types, and the concrete types that implement or extend these types.

Существуют также другие типы внедрения зависимостей, таких как путем внедрения кода метода задания свойства, и путем внедрения кода вызов метода, но они видны редко.There are also other types of dependency injection, such as property setter injection, and method call injection, but they are less commonly seen. Таким образом в этой главе основное внимание уделяется исключительно выполнения внедрение конструктора с помощью контейнера внедрения зависимостей.Therefore, this chapter will focus solely on performing constructor injection with a dependency injection container.

Общие сведения о внедрения зависимостейIntroduction to Dependency Injection

Внедрение зависимостей является специальной версией шаблона Inversion of Control (IoC), где есть опасение Инвертированная — это процесс получения необходимую зависимость.Dependency injection is a specialized version of the Inversion of Control (IoC) pattern, where the concern being inverted is the process of obtaining the required dependency. С помощью внедрения зависимостей другой класс отвечает за внедрение зависимостей в объект во время выполнения.With dependency injection, another class is responsible for injecting dependencies into an object at runtime. В следующем примере кода показано как ProfileViewModel класс структурирован, при использовании внедрения зависимостей:The following code example shows how the ProfileViewModel class is structured when using dependency injection:

public class ProfileViewModel : ViewModelBase  
{  
    private IOrderService _orderService;  

    public ProfileViewModel(IOrderService orderService)  
    {  
        _orderService = orderService;  
    }  
    ...  
}

ProfileViewModel Конструктор получает IOrderService экземпляр в качестве аргумента, внедрены в другом классе.The ProfileViewModel constructor receives an IOrderService instance as an argument, injected by another class. Единственная зависимость в ProfileViewModel класс находится в типе интерфейса.The only dependency in the ProfileViewModel class is on the interface type. Таким образом ProfileViewModel класс не имеет каких-либо знаний класса, который отвечает за создание экземпляра IOrderService объекта.Therefore, the ProfileViewModel class doesn't have any knowledge of the class that's responsible for instantiating the IOrderService object. Класс, который отвечает за создание экземпляра IOrderService объекта и вставить эти данные в ProfileViewModel класса, называется контейнера внедрения зависимостей.The class that's responsible for instantiating the IOrderService object, and inserting it into the ProfileViewModel class, is known as the dependency injection container.

Контейнеры для введения зависимостей уменьшить взаимозависимость между объектами, предоставляя средства для создания экземпляров класса и управления временем их существования, в зависимости от конфигурации контейнера.Dependency injection containers reduce the coupling between objects by providing a facility to instantiate class instances and manage their lifetime based on the configuration of the container. Во время создания объекта контейнера внедряет все зависимости, требуемые для объекта требуется в нее.During the objects creation, the container injects any dependencies that the object requires into it. Если эти зависимости не был создан, создает контейнер и сначала устраняет их зависимости.If those dependencies have not yet been created, the container creates and resolves their dependencies first.

Примечание

Внедрение зависимостей может также быть реализован вручную с помощью фабрик.Dependency injection can also be implemented manually using factories. Тем не менее используя контейнер предоставляет дополнительные возможности, такие как управление временем существования и регистрации сборки сканирования.However, using a container provides additional capabilities such as lifetime management, and registration through assembly scanning.

Существует несколько преимуществ в использовании контейнера внедрения зависимостей.There are several advantages to using a dependency injection container:

  • Контейнер избавляет от необходимости для класса найти его зависимости и управлять временем их существования.A container removes the need for a class to locate its dependencies and manage their lifetimes.
  • Контейнер позволяет сопоставление зависимостей, реализованных без влияния на класс.A container allows mapping of implemented dependencies without affecting the class.
  • Контейнер реализует возможности тестирования, позволяя зависимости макетирование.A container facilitates testability by allowing dependencies to be mocked.
  • Контейнер повышает удобство поддержки, позволяя новые классы, позволяющие легко добавить в приложение.A container increases maintainability by allowing new classes to be easily added to the app.

В контексте приложения Xamarin.Forms, которое использует шаблон MVVM контейнер внедрения зависимостей будет обычно использоваться для регистрации и разрешения на просмотр моделей, а также для регистрации служб и вставляя их в модели представления.In the context of a Xamarin.Forms app that uses MVVM, a dependency injection container will typically be used for registering and resolving view models, and for registering services and injecting them into view models.

Доступно множество контейнеры для введения зависимостей, используя мобильное приложение eShopOnContainers использует Autofac для управления созданием экземпляров модели представления и классы в приложении службы.There are many dependency injection containers available, with the eShopOnContainers mobile app using Autofac to manage the instantiation of view model and service classes in the app. Autofac вспомогательного при создании слабо связанных приложений и предоставляет все функции, которые часто встречаются в контейнеры для введения зависимостей, включая методы для регистрации сопоставления типов и экземпляров объектов, разрешения объектов, управление временем существования объектов и внедрить зависимые объекты в конструкторы объектов, которые он разрешает.Autofac facilitates building loosely coupled apps, and provides all of the features commonly found in dependency injection containers, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into constructors of objects that it resolves. Дополнительные сведения о Autofac, см. в разделе Autofac на сайте readthedocs.io.For more information about Autofac, see Autofac on readthedocs.io.

В Autofac IContainer интерфейс предоставляет контейнера внедрения зависимостей.In Autofac, the IContainer interface provides the dependency injection container. Рис. 3-1 показаны зависимости, при использовании этого контейнера, который создает экземпляр IOrderService объекта и вставляет его в ProfileViewModel класса.Figure 3-1 shows the dependencies when using this container, which instantiates an IOrderService object and injects it into the ProfileViewModel class.

Рис. 3-1. Зависимости при использовании внедрения зависимостейFigure 3-1: Dependencies when using dependency injection

Во время выполнения, контейнер необходимо знать, какую реализацию из IOrderService интерфейс, он должен создать экземпляр, прежде чем его можно создать экземпляр ProfileViewModel объекта.At runtime, the container must know which implementation of the IOrderService interface it should instantiate, before it can instantiate a ProfileViewModel object. Это включает в себя:This involves:

  • Выбор способа создания экземпляра объекта, реализующего контейнера IOrderService интерфейс.The container deciding how to instantiate an object that implements the IOrderService interface. Этот процесс называется регистрации.This is known as registration.
  • Контейнер, создание экземпляра объекта, который реализует IOrderService интерфейс и ProfileViewModel объекта.The container instantiating the object that implements the IOrderService interface, and the ProfileViewModel object. Этот процесс называется разрешение.This is known as resolution.

Со временем, приложение будет готово, с помощью ProfileViewModel объектом и он станет доступным для сборки мусора.Eventually, the app will finish using the ProfileViewModel object and it will become available for garbage collection. На этом этапе следует освободить сборщик мусора IOrderService экземпляра, если другие классы, не используйте тот же экземпляр.At this point, the garbage collector should dispose of the IOrderService instance if other classes do not share the same instance.

Совет

Напишите код, независимый от контейнера.Write container-agnostic code. Всегда пытаться написать код независимой от контейнера, чтобы отделить приложение из контейнера конкретных зависимостей используется.Always try to write container-agnostic code to decouple the app from the specific dependency container being used.

РегистрацияRegistration

Прежде чем зависимости могут внедряться в объект, типы зависимостей сначала должны регистрироваться с контейнером.Before dependencies can be injected into an object, the types of the dependencies must first be registered with the container. Регистрация типа обычно включает в себя передачи в контейнер интерфейса и конкретный тип, реализующий интерфейс.Registering a type typically involves passing the container an interface and a concrete type that implements the interface.

Существует два способа регистрации типов и объектов в контейнере с помощью кода:There are two ways of registering types and objects in the container through code:

  • Зарегистрируйте тип или сопоставление с контейнером.Register a type or mapping with the container. При необходимости, контейнер будет создавать экземпляр указанного типа.When required, the container will build an instance of the specified type.
  • Регистрация существующего объекта в контейнере как единственный экземпляр.Register an existing object in the container as a singleton. При необходимости контейнера будет возвращать ссылку на существующий объект.When required, the container will return a reference to the existing object.

Совет

Контейнеры для введения зависимостей не всегда подходит.Dependency injection containers are not always suitable. Внедрение зависимостей представляет дополнительные сложности и требования, которые не могут быть соответствующие или полезные для небольших приложений.Dependency injection introduces additional complexity and requirements that might not be appropriate or useful to small apps. Если класс не имеет зависимые компоненты, или не является зависимость для других типов, не может быть целесообразно поместить его в контейнере.If a class does not have any dependencies, or is not a dependency for other types, it might not make sense to put it in the container. Кроме того Если класс имеет один набор зависимостей, которые являются неотъемлемой частью типа и никогда не изменится, не может быть целесообразно, чтобы поместить его в контейнер.In addition, if a class has a single set of dependencies that are integral to the type and will never change, it might not make sense to put it in the container.

Следует выполнять регистрацию типов, требующих внедрение зависимостей в единственном методе в приложении, и этот метод должен вызываться на ранних этапах жизненного цикла приложения, чтобы убедиться, что приложение будет знать о зависимости между его классы.The registration of types that require dependency injection should be performed in a single method in an app, and this method should be invoked early in the app's lifecycle to ensure that the app is aware of the dependencies between its classes. В мобильном приложении eShopOnContainers это выполняется с помощью ViewModelLocator класса, какие сборки IContainer объекта и является единственным классом в приложение, которое содержит ссылку на этот объект.In the eShopOnContainers mobile app this is performed by the ViewModelLocator class, which builds the IContainer object and is the only class in the app that holds a reference to that object. В следующем примере кода показано, как мобильное приложение eShopOnContainers объявляет IContainer объекта в ViewModelLocator класса:The following code example shows how the eShopOnContainers mobile app declares the IContainer object in the ViewModelLocator class:

private static IContainer _container;

Типы и экземпляры регистрируются в RegisterDependencies метод в ViewModelLocator класса.Types and instances are registered in the RegisterDependencies method in the ViewModelLocator class. Это достигается путем предварительного создания ContainerBuilder экземпляра, как показано в следующем примере кода:This is achieved by first creating a ContainerBuilder instance, which is demonstrated in the following code example:

var builder = new ContainerBuilder();

Типы и экземпляры затем регистрируется ContainerBuilder объекта, а также в следующем примере кода показано, наиболее распространенной формой типа регистрации:Types and instances are then registered with the ContainerBuilder object, and the following code example demonstrates the most common form of type registration:

builder.RegisterType<RequestProvider>().As<IRequestProvider>();

RegisterType Приведенный здесь метод является типом интерфейса сопоставляется с конкретным типом.The RegisterType method shown here maps an interface type to a concrete type. Сообщает контейнеру, для создания экземпляра RequestProvider объекта, когда он создает экземпляр объекта, который требует внедрение IRequestProvider через конструктор.It tells the container to instantiate a RequestProvider object when it instantiates an object that requires an injection of an IRequestProvider through a constructor.

Конкретные типы также могут быть зарегистрированы напрямую без сопоставления из типа интерфейса, как показано в следующем примере кода:Concrete types can also be registered directly without a mapping from an interface type, as shown in the following code example:

builder.RegisterType<ProfileViewModel>();

Когда ProfileViewModel тип разрешена, контейнер будет внедрять его необходимые зависимости.When the ProfileViewModel type is resolved, the container will inject its required dependencies.

Autofac также позволяет регистрация экземпляра, где контейнер отвечает за ведение ссылку на одноэлементный экземпляр типа.Autofac also allows instance registration, where the container is responsible for maintaining a reference to a singleton instance of a type. Например, в следующем примере кода показано, как мобильное приложение eShopOnContainers регистрирует конкретный тип для использования при ProfileViewModel экземпляр должен иметь IOrderService экземпляр:For example, the following code example shows how the eShopOnContainers mobile app registers the concrete type to use when a ProfileViewModel instance requires an IOrderService instance:

builder.RegisterType<OrderService>().As<IOrderService>().SingleInstance();

RegisterType Приведенный здесь метод является типом интерфейса сопоставляется с конкретным типом.The RegisterType method shown here maps an interface type to a concrete type. SingleInstance Метод настроить регистрацию таким образом, чтобы один и тот же общий экземпляр получает все зависимые объекты.The SingleInstance method configures the registration so that every dependent object receives the same shared instance. Таким образом только один OrderService экземпляр будет существовать в контейнере, который совместно используется объектами, которые требуют внедрение IOrderService через конструктор.Therefore, only a single OrderService instance will exist in the container, which is shared by objects that require an injection of an IOrderService through a constructor.

Регистрация экземпляра также может быть выполнена с RegisterInstance метод, как показано в следующем примере кода:Instance registration can also be performed with the RegisterInstance method, which is demonstrated in the following code example:

builder.RegisterInstance(new OrderMockService()).As<IOrderService>();

RegisterInstance Приведенный здесь метод создает новую OrderMockService экземпляра и регистрирует его с контейнером.The RegisterInstance method shown here creates a new OrderMockService instance and registers it with the container. Таким образом только один OrderMockService экземпляр существует в контейнере, который совместно используется объектами, которые требуют внедрение IOrderService через конструктор.Therefore, only a single OrderMockService instance exists in the container, which is shared by objects that require an injection of an IOrderService through a constructor.

После регистрации типа и экземпляра, IContainer объекта должны быть построены, который показан в следующем примере кода:Following type and instance registration, the IContainer object must be built, which is demonstrated in the following code example:

_container = builder.Build();

Вызов Build метод ContainerBuilder экземпляр создает новый контейнер внедрения зависимостей, содержащий регистрации, которые были внесены.Invoking the Build method on the ContainerBuilder instance builds a new dependency injection container that contains the registrations that have been made.

Совет

Рассмотрите возможность IContainer как неизменяемый.Consider an IContainer as being immutable. Тогда как Autofac предоставляет Update метода, чтобы обновить регистрацию в существующий контейнер, вызов этого метода следует избегать, где это возможно.While Autofac provides an Update method to update registrations in an existing container, calling this method should be avoided where possible. Существуют риски для изменения контейнера после был построен, особенно в том случае, если используется контейнер.There are risks to modifying a container after it's been built, particularly if the container has been used. Дополнительные сведения см. в разделе рассмотрим контейнере, как неизменяемые на сайте readthedocs.io.For more information, see Consider a Container as Immutable on readthedocs.io.

РешениеResolution

После регистрации тип, его можно разрешить или внедрены в качестве зависимости.After a type is registered, it can be resolved or injected as a dependency. Когда необходимо разрешить тип и контейнер должен создать новый экземпляр, оно внедряет зависимые компоненты в экземпляре.When a type is being resolved and the container needs to create a new instance, it injects any dependencies into the instance.

Как правило при разрешении типа одно из трех действий происходит:Generally, when a type is resolved, one of three things happens:

  1. Если тип не был зарегистрирован, контейнер вызывает исключение.If the type hasn't been registered, the container throws an exception.
  2. Если тип был зарегистрирован как единственный экземпляр, контейнер возвращает одноэлементный экземпляр.If the type has been registered as a singleton, the container returns the singleton instance. Если это в первый раз вызов для метода типа, контейнер создает ее при необходимости и сохраняет ссылку на него.If this is the first time the type is called for, the container creates it if required, and maintains a reference to it.
  3. Если тип не был зарегистрирован как единственный экземпляр, контейнер возвращает новый экземпляр и не поддерживает ссылки на него.If the type hasn't been registered as a singleton, the container returns a new instance, and doesn't maintain a reference to it.

В следующем примере кода показано как RequestProvider тип, который был зарегистрирован ранее Autofac можно разрешить:The following code example shows how the RequestProvider type that was previously registered with Autofac can be resolved:

var requestProvider = _container.Resolve<IRequestProvider>();

В этом примере Autofac будет предложено разрешить конкретный тип для IRequestProvider типа, а также любые зависимости.In this example, Autofac is asked to resolve the concrete type for the IRequestProvider type, along with any dependencies. Как правило Resolve метод вызывается, когда требуется экземпляр определенного типа.Typically, the Resolve method is called when an instance of a specific type is required. Сведения об управлении временем существования объектов разрешения, см. в разделе управления временем существования объекта разрешен объектами.For information about controlling the lifetime of resolved objects, see Managing the Lifetime of Resolved Objects.

В следующем примере кода показано, как мобильное приложение eShopOnContainers создает экземпляры типов модели представления и их зависимости:The following code example shows how the eShopOnContainers mobile app instantiates view model types and their dependencies:

var viewModel = _container.Resolve(viewModelType);

В этом примере Autofac будет предложено разрешить тип модели представления для модели требуемого представления и контейнер также разрешается зависимые компоненты.In this example, Autofac is asked to resolve the view model type for a requested view model, and the container will also resolve any dependencies. При разрешении ProfileViewModel , устранить зависимость от измеряется IOrderService объекта.When resolving the ProfileViewModel type, the dependency to resolve is an IOrderService object. Таким образом, сначала создает Autofac OrderService и затем передает его в конструктор ProfileViewModel класса.Therefore, Autofac first constructs an OrderService object and then passes it to the constructor of the ProfileViewModel class. Дополнительные сведения о том, как мобильное приложение eShopOnContainers создает представление модели и связывает их с представлениями, см. в разделе автоматическое создание модели представления с указателем модели представления.For more information about how the eShopOnContainers mobile app constructs view models and associates them to views, see Automatically Creating a View Model with a View Model Locator.

Примечание

Регистрация и разрешение типов с помощью контейнера имеет к снижению производительности из-за контейнера использование отражения для создания каждого типа, особенно в том случае, если зависимости воссоздаются для каждого Навигация по страницам в приложении.Registering and resolving types with a container has a performance cost because of the container's use of reflection for creating each type, especially if dependencies are being reconstructed for each page navigation in the app. Если существует много или всех зависимостей, затраты на создание может значительно возрасти.If there are many or deep dependencies, the cost of creation can increase significantly.

Управление временем существования объектов разрешенаManaging the Lifetime of Resolved Objects

После регистрации типа, поведение по умолчанию для Autofac — для создания нового экземпляра зарегистрированного типа каждый раз типа разрешается или когда механизмом зависимостей внедряет экземпляров в другие классы.After registering a type, the default behavior for Autofac is to create a new instance of the registered type each time the type is resolved, or when the dependency mechanism injects instances into other classes. В этом случае контейнер не хранит ссылку на объект разрешения.In this scenario, the container doesn't hold a reference to the resolved object. Тем не менее при регистрации экземпляра, поведение по умолчанию для Autofac является управление временем существования объекта как единственный экземпляр.However, when registering an instance, the default behavior for Autofac is to manage the lifetime of the object as a singleton. Таким образом экземпляр остается в области, пока контейнер находится в области и удаляется, когда контейнер выходит за пределы области и обрабатывается сборщиком мусора или код явно удаляет контейнер.Therefore, the instance remains in scope while the container is in scope, and is disposed when the container goes out of scope and is garbage collected, or when code explicitly disposes the container.

Областью Autofac экземпляр может использоваться для задания алгоритма одноэлементный объект, который создает Autofac из зарегистрированного типа.An Autofac instance scope can be used to specify the singleton behavior for an object that Autofac creates from a registered type. Autofac областями действия экземпляра управляют временем жизни объектов, создаваться контейнером.Autofac instance scopes manage the object lifetimes instantiated by the container. Область экземпляра по умолчанию для RegisterType метод InstancePerDependency области.The default instance scope for the RegisterType method is the InstancePerDependency scope. Тем не менее SingleInstance область может использоваться с RegisterType метод, таким образом, создает контейнер, или возвращает одноэлементный экземпляр типа, при вызове Resolve метод.However, the SingleInstance scope can be used with the RegisterType method, so that the container creates or returns a singleton instance of a type when calling the Resolve method. В следующем примере кода показано, как Autofac дано указание создать одноэлементный экземпляр NavigationService класса:The following code example shows how Autofac is instructed to create a singleton instance of the NavigationService class:

builder.RegisterType<NavigationService>().As<INavigationService>().SingleInstance();

При первой INavigationService интерфейс разрешена, создает новый контейнер NavigationService объекта и сохраняет ссылку на него.The first time that the INavigationService interface is resolved, the container creates a new NavigationService object and maintains a reference to it. На любые последующие разрешение INavigationService интерфейс, контейнер возвращает ссылку на NavigationService объект, который был создан ранее.On any subsequent resolutions of the INavigationService interface, the container returns a reference to the NavigationService object that was previously created.

Примечание

Область SingleInstance удаляет созданные объекты при удалении контейнера.The SingleInstance scope disposes created objects when the container is disposed.

Autofac включает дополнительный экземпляр области.Autofac includes additional instance scopes. Дополнительные сведения см. в разделе область экземпляра на сайте readthedocs.io.For more information, see Instance Scope on readthedocs.io.

СводкаSummary

Внедрение зависимостей обеспечивает разделение конкретные типы из кода, который зависит от этих типов.Dependency injection enables decoupling of concrete types from the code that depends on these types. Обычно в нем контейнер, который содержит список регистраций и сопоставления между интерфейсы и абстрактные типы и конкретные типы, которые реализуют или расширяют эти типы.It typically uses a container that holds a list of registrations and mappings between interfaces and abstract types, and the concrete types that implement or extend these types.

Autofac вспомогательного при создании слабо связанных приложений и предоставляет все функции, которые часто встречаются в контейнеры для введения зависимостей, включая методы для регистрации сопоставления типов и экземпляров объектов, разрешения объектов, управление временем существования объектов и внедрить зависимые объекты в конструкторы объектов, которые он разрешает.Autofac facilitates building loosely coupled apps, and provides all of the features commonly found in dependency injection containers, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into constructors of objects it resolves.