Разрешение зависимостей в Xamarin.FormsDependency resolution in Xamarin.Forms

Загрузить образец загрузить примерDownload Sample Download the sample

В этой статье объясняется, как вставить метод разрешения зависимостей в Xamarin.Forms, таким образом, чтобы контейнера внедрения зависимостей приложения имеет контроль над созданием и временем существования пользовательские модули подготовки отчетов, эффекты и DependencyService реализации. В примерах кода в этой статье взяты из разрешение зависимостей с помощью контейнеров образца.This article explains how to inject a dependency resolution method into Xamarin.Forms so that an application's dependency injection container has control over the creation and lifetime of custom renderers, effects, and DependencyService implementations. The code examples in this article are taken from the Dependency Resolution using Containers sample.

Контейнер внедрения зависимостей в контексте приложения Xamarin.Forms, использующая шаблон Model-View-ViewModel (MVVM), можно использовать для регистрации и разрешения на просмотр моделей, а также для регистрации служб и вставляя их в модели представления.In the context of a Xamarin.Forms application that uses the Model-View-ViewModel (MVVM) pattern, a dependency injection container can be used for registering and resolving view models, and for registering services and injecting them into view models. Во время создания модели представления контейнер внедряет зависимые компоненты, которые необходимы.During view model creation, the container injects any dependencies that are required. Если не были созданы эти зависимости, создает контейнер и сначала устраняет зависимости.If those dependencies have not been created, the container creates and resolves the dependencies first. Дополнительные сведения о внедрения зависимостей, включая примеры внедрение зависимостей в модели представления, см. в разделе внедрения зависимостей.For more information about dependency injection, including examples of injecting dependencies into view models, see Dependency Injection.

Контроль над созданием и временем существования типов в проектах платформы обычно осуществляется Xamarin.Forms, который использует Activator.CreateInstance метод, чтобы создать экземпляры пользовательских модулей подготовки отчетов, эффекты, и DependencyService в реализации.Control over the creation and lifetime of types in platform projects is traditionally performed by Xamarin.Forms, which uses the Activator.CreateInstance method to create instances of custom renderers, effects, and DependencyService implementations. К сожалению это ограничивает контроль разработчика над созданием и временем существования этих типов, а также возможность внедрения зависимостей в них.Unfortunately, this limits developer control over the creation and lifetime of these types, and the ability to inject dependencies into them. Это поведение можно изменить, внедряя метод разрешения зависимостей в Xamarin.Forms, который определяет, как будут созданы типы — или контейнера внедрения зависимостей приложения, по Xamarin.Forms.This behavior can be changed by injecting a dependency resolution method into Xamarin.Forms that controls how types will be created – either by the application's dependency injection container, or by Xamarin.Forms. Тем не менее Обратите внимание на то, что нет необходимости внедрять метод разрешения зависимостей в Xamarin.Forms.However, note that there is no requirement to inject a dependency resolution method into Xamarin.Forms. Xamarin.Forms будет продолжать создавать и управлять временем существования типов в проектах платформы, если метод разрешения зависимостей не вставлен.Xamarin.Forms will continue to create and manage the lifetime of types in platform projects if a dependency resolution method isn't injected.

Примечание

Хотя эта статья посвящена Включение метод разрешения зависимостей в Xamarin.Forms, которое разрешается зарегистрированные типы, с помощью контейнера внедрения зависимостей, можно также внедрить метод разрешения зависимостей, который использует фабричные методы для решения Зарегистрированные типы.While this article focuses on injecting a dependency resolution method into Xamarin.Forms that resolves registered types using a dependency injection container, it's also possible to inject a dependency resolution method that uses factory methods to resolve registered types. Дополнительные сведения см. в разделе разрешение зависимостей с помощью методов фабрики образца.For more information, see the Dependency Resolution using Factory Methods sample.

Добавление метода разрешения зависимостейInjecting a dependency resolution method

DependencyResolver Класс предоставляет возможность внедрения зависимостей метод разрешения в Xamarin.Forms, используя ResolveUsing метод.The DependencyResolver class provides the ability to inject a dependency resolution method into Xamarin.Forms, using the ResolveUsing method. Затем когда Xamarin.Forms требуется экземпляр определенного типа, этот метод разрешения зависимостей присваивается возможность предоставить экземпляр.Then, when Xamarin.Forms needs an instance of a particular type, the dependency resolution method is given the opportunity to provide the instance. Если этот метод разрешения зависимостей возвращает null для запрошенного типа, возвращается Xamarin.Forms к попытке создать тип экземпляр с помощью Activator.CreateInstance метод.If the dependency resolution method returns null for a requested type, Xamarin.Forms falls back to attempting to create the type instance itself using the Activator.CreateInstance method.

В следующем примере показано, как установить метод разрешения зависимостей с ResolveUsing метод:The following example shows how to set the dependency resolution method with the ResolveUsing method:

using Autofac;
using Xamarin.Forms.Internals;
...

public partial class App : Application
{
    // IContainer and ContainerBuilder are provided by Autofac
    static IContainer container;
    static readonly ContainerBuilder builder = new ContainerBuilder();

    public App()
    {
        ...
        DependencyResolver.ResolveUsing(type => container.IsRegistered(type) ? container.Resolve(type) : null);
        ...
    }
    ...
}

В этом примере метод разрешения зависимостей имеет значение лямбда-выражения и использует Autofac контейнера внедрения зависимостей, чтобы разрешить все типы, которые были зарегистрированы с контейнером.In this example, the dependency resolution method is set to a lambda expression that uses the Autofac dependency injection container to resolve any types that have been registered with the container. В противном случае null будет возвращаться, что приведет к попытке разрешения типа Xamarin.Forms.Otherwise, null will be returned, which will result in Xamarin.Forms attempting to resolve the type.

Примечание

API, используемые контейнер внедрения зависимостей относится к контейнеру.The API used by a dependency injection container is specific to the container. В примерах кода в этой статье использовать Autofac в качестве контейнера внедрения зависимостей, который предоставляет IContainer и ContainerBuilder типов.The code examples in this article use Autofac as a dependency injection container, which provides the IContainer and ContainerBuilder types. Контейнеры для введения зависимостей альтернативные столь же может использоваться, но используются разные интерфейсы API, чем представлены здесь.Alternative dependency injection containers could equally be used, but would use different APIs than are presented here.

Обратите внимание, что не требуется, чтобы установить метод разрешения зависимостей во время запуска приложения.Note that there is no requirement to set the dependency resolution method during application startup. Его можно установить в любое время.It can be set at any time. Единственное ограничение — это что Xamarin.Forms необходимо знать о метод разрешения зависимостей, время, когда приложение пытается использовать типы, хранящиеся в контейнер внедрения зависимостей.The only constraint is that Xamarin.Forms needs to know about the dependency resolution method by the time that the application attempts to consume types stored in the dependency injection container. Таким образом Если в контейнер внедрения зависимостей, который требует наличия приложения во время запуска службы, этот метод разрешения зависимостей будет устанавливаться на ранних этапах жизненного цикла приложения.Therefore, if there are services in the dependency injection container that the application will require during startup, the dependency resolution method will have to be set early in the application's lifecycle. Аналогичным образом Если контейнер внедрения зависимостей управляет созданием и временем существования конкретного Effect , необходимо знать о методе разрешения зависимостей, перед попыткой создания представления Xamarin.Forms, использует его Effect.Similarly, if the dependency injection container manages the creation and lifetime of a particular Effect, Xamarin.Forms will need to know about the dependency resolution method before it attempts to create a view that uses that Effect.

Предупреждение

Регистрация и разрешение типов с помощью контейнера внедрения зависимостей имеет к снижению производительности из-за контейнера использование отражения для создания каждого типа, особенно в том случае, если зависимости воссоздаются для каждого Навигация по страницам в приложении.Registering and resolving types with a dependency injection 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 application. Если существует много или всех зависимостей, затраты на создание может значительно возрасти.If there are many or deep dependencies, the cost of creation can increase significantly.

Регистрация типовRegistering types

Типы должны быть зарегистрированы с помощью контейнера внедрения зависимостей, перед разрешением их через метод разрешения зависимостей.Types must be registered with the dependency injection container before it can resolve them via the dependency resolution method. В следующем примере кода показаны методы регистрации, что пример приложения предоставляет в App класс для контейнер Autofac:The following code example shows the registration methods that the sample application exposes in the App class, for the Autofac container:

using Autofac;
using Autofac.Core;
...

public partial class App : Application
{
    static IContainer container;
    static readonly ContainerBuilder builder = new ContainerBuilder();
    ...

    public static void RegisterType<T>() where T : class
    {
        builder.RegisterType<T>();
    }

    public static void RegisterType<TInterface, T>() where TInterface : class where T : class, TInterface
    {
        builder.RegisterType<T>().As<TInterface>();
    }

    public static void RegisterTypeWithParameters<T>(Type param1Type, object param1Value, Type param2Type, string param2Name) where T : class
    {
        builder.RegisterType<T>()
               .WithParameters(new List<Parameter>()
        {
            new TypedParameter(param1Type, param1Value),
            new ResolvedParameter(
                (pi, ctx) => pi.ParameterType == param2Type && pi.Name == param2Name,
                (pi, ctx) => ctx.Resolve(param2Type))
        });
    }

    public static void RegisterTypeWithParameters<TInterface, T>(Type param1Type, object param1Value, Type param2Type, string param2Name) where TInterface : class where T : class, TInterface
    {
        builder.RegisterType<T>()
               .WithParameters(new List<Parameter>()
        {
            new TypedParameter(param1Type, param1Value),
            new ResolvedParameter(
                (pi, ctx) => pi.ParameterType == param2Type && pi.Name == param2Name,
                (pi, ctx) => ctx.Resolve(param2Type))
        }).As<TInterface>();
    }

    public static void BuildContainer()
    {
        container = builder.Build();
    }
    ...
}

Когда приложение использует метод разрешение зависимостей для разрешения типов из контейнера, тип регистрации обычно выполняются из проектов платформы.When an application uses a dependency resolution method to resolve types from a container, type registrations are typically performed from platform projects. Это позволяет платформы проектов зарегистрировать типы для пользовательских модулей подготовки отчетов, эффекты, и DependencyService реализаций.This enables platform projects to register types for custom renderers, effects, and DependencyService implementations.

После регистрации типа в проекте платформы IContainer объекта должны быть построены, который выполняется путем вызова метода BuildContainer метод.Following type registration from a platform project, the IContainer object must be built, which is accomplished by calling the BuildContainer method. Этот метод вызывает метод в Autofac Build метод ContainerBuilder экземпляр, который создает новый контейнер внедрения зависимостей, содержащий регистрации, которые были внесены.This method invokes Autofac's Build method on the ContainerBuilder instance, which builds a new dependency injection container that contains the registrations that have been made.

В последующих разделах Logger класс, реализующий ILogger интерфейс внедряется в конструкторы класса.In the sections that follow, a Logger class that implements the ILogger interface is injected into class constructors. Logger Функциональность реализует простую регистрацию класса с помощью Debug.WriteLine метод и используется для демонстрации того, как службы могут внедряться в пользовательские отрисовщики, эффекты, и DependencyService реализаций.The Logger class implements simple logging functionality using the Debug.WriteLine method, and is used to demonstrate how services can be injected into custom renderers, effects, and DependencyService implementations.

Регистрация пользовательских модулей подготовки отчетовRegistering custom renderers

Образец приложения включает страницы, который играет веб-видео, источником которого XAML показан в следующем примере:The sample application includes a page that plays web videos, whose XAML source is shown in the following example:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:video="clr-namespace:FormsVideoLibrary"
             ...>
    <video:VideoPlayer Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
</ContentPage>

VideoPlayer Представление реализуется на каждой платформе, VideoPlayerRenderer класса, который предоставляет функциональные возможности для воспроизведения видео.The VideoPlayer view is implemented on each platform by a VideoPlayerRenderer class, that provides the functionality for playing the video. Дополнительные сведения об этих классах пользовательское средство отрисовки см. в разделе реализация видеопроигрывателя.For more information about these custom renderer classes, see Implementing a video player.

В iOS и универсальной Windows платформы (UWP) VideoPlayerRenderer классы имеют следующий конструктор, который требует ILogger аргумент:On iOS and the Universal Windows Platform (UWP), the VideoPlayerRenderer classes have the following constructor, which requires an ILogger argument:

public VideoPlayerRenderer(ILogger logger)
{
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

На всех платформах, осуществляется регистрация типа с помощью контейнера внедрения зависимостей RegisterTypes метод, который вызывается до загрузки приложения с платформы LoadApplication(new App()) метод.On all the platforms, type registration with the dependency injection container is performed by the RegisterTypes method, which is invoked prior to the platform loading the application with the LoadApplication(new App()) method. В следующем примере показан RegisterTypes метод на платформе iOS:The following example shows the RegisterTypes method on the iOS platform:

void RegisterTypes()
{
    App.RegisterType<ILogger, Logger>();
    App.RegisterType<FormsVideoLibrary.iOS.VideoPlayerRenderer>();
    App.BuildContainer();
}

В этом примере Logger конкретный тип зарегистрирован с помощью сопоставления с его тип интерфейса и VideoPlayerRenderer напрямую без сопоставления интерфейс зарегистрирован тип.In this example, the Logger concrete type is registered via a mapping against its interface type, and the VideoPlayerRenderer type is registered directly without an interface mapping. Когда пользователь переходит на страницу, содержащую VideoPlayer представление, этот метод разрешения зависимостей будет вызываться для разрешения VideoPlayerRenderer типа из контейнера внедрения зависимостей, которая также устранить и внедрить Logger введите в VideoPlayerRenderer конструктор.When the user navigates to the page containing the VideoPlayer view, the dependency resolution method will be invoked to resolve the VideoPlayerRenderer type from the dependency injection container, which will also resolve and inject the Logger type into the VideoPlayerRenderer constructor.

VideoPlayerRenderer Конструктор на платформе Android немного сложнее, так как требует Context аргумент в дополнение к ILogger аргумент:The VideoPlayerRenderer constructor on the Android platform is slightly more complicated as it requires a Context argument in addition to the ILogger argument:

public VideoPlayerRenderer(Context context, ILogger logger) : base(context)
{
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

В следующем примере показан RegisterTypes метод на платформе Android:The following example shows the RegisterTypes method on the Android platform:

void RegisterTypes()
{
    App.RegisterType<ILogger, Logger>();
    App.RegisterTypeWithParameters<FormsVideoLibrary.Droid.VideoPlayerRenderer>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
    App.BuildContainer();
}

В этом примере App.RegisterTypeWithParameters регистры метод VideoPlayerRenderer с помощью контейнера внедрения зависимостей.In this example, the App.RegisterTypeWithParameters method registers the VideoPlayerRenderer with the dependency injection container. Метод регистрации гарантирует, что MainActivity экземпляр внедряются как Context аргумента и создает Logger тип внедряются как ILogger аргумент.The registration method ensures that the MainActivity instance will be injected as the Context argument, and that the Logger type will be injected as the ILogger argument.

Регистрация эффектыRegistering effects

Образец приложения включает страницы, использующей отслеживания эффект сенсорного ввода для перетаскивания BoxView экземпляры по странице.The sample application includes a page that uses a touch tracking effect to drag BoxView instances around the page. Effect Добавляется BoxView следующим образом:The Effect is added to the BoxView using the following code:

var boxView = new BoxView { ... };
var touchEffect = new TouchEffect();
boxView.Effects.Add(touchEffect);

TouchEffect Класс является RoutingEffect , реализованный на каждой платформе, TouchEffect класс, который имеет PlatformEffect.The TouchEffect class is a RoutingEffect that's implemented on each platform by a TouchEffect class that's a PlatformEffect. Платформа TouchEffect класс предоставляет функциональные возможности для перетаскивания BoxView вокруг страницы.The platform TouchEffect class provides the functionality for dragging the BoxView around the page. Дополнительные сведения об этих классах эффект см. в разделе вызов события из эффекты.For more information about these effect classes, see Invoking events from effects.

На всех платформах TouchEffect класс имеет следующий конструктор, который требует ILogger аргумент:On all the platforms, the TouchEffect class has the following constructor, which requires an ILogger argument:

public TouchEffect(ILogger logger)
{
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

На всех платформах, осуществляется регистрация типа с помощью контейнера внедрения зависимостей RegisterTypes метод, который вызывается до загрузки приложения с платформы LoadApplication(new App()) метод.On all the platforms, type registration with the dependency injection container is performed by the RegisterTypes method, which is invoked prior to the platform loading the application with the LoadApplication(new App()) method. В следующем примере показан RegisterTypes метод на платформе Android:The following example shows the RegisterTypes method on the Android platform:

void RegisterTypes()
{
    App.RegisterType<ILogger, Logger>();
    App.RegisterType<TouchTracking.Droid.TouchEffect>();
    App.BuildContainer();
}

В этом примере Logger конкретный тип зарегистрирован с помощью сопоставления с его тип интерфейса и TouchEffect напрямую без сопоставления интерфейс зарегистрирован тип.In this example, the Logger concrete type is registered via a mapping against its interface type, and the TouchEffect type is registered directly without an interface mapping. Когда пользователь переходит на страницу, содержащую BoxView экземпляра, включающего TouchEffect подключенные к ней, этот метод разрешения зависимостей будет вызываться для решения на платформу TouchEffect тип из зависимостей Внедрение контейнера, который также устранить и внедрить Logger введите в TouchEffect конструктор.When the user navigates to the page containing a BoxView instance that has the TouchEffect attached to it, the dependency resolution method will be invoked to resolve the platform TouchEffect type from the dependency injection container, which will also resolve and inject the Logger type into the TouchEffect constructor.

Регистрация реализаций DependencyServiceRegistering DependencyService implementations

Образец приложения включает страницы, использующей DependencyService реализаций на каждой платформе, чтобы разрешить пользователю выбрать снимок из библиотеки рисунков устройства.The sample application includes a page that uses DependencyService implementations on each platform to allow the user to pick a photo from the device's picture library. IPhotoPicker Интерфейс определяет функциональность, которая реализуется DependencyService реализации и показано в следующем примере:The IPhotoPicker interface defines the functionality that is implemented by the DependencyService implementations, and is shown in the following example:

public interface IPhotoPicker
{
    Task<Stream> GetImageStreamAsync();
}

В каждом проекте платформы PhotoPicker класс реализует IPhotoPicker интерфейса с помощью API платформы.In each platform project, the PhotoPicker class implements the IPhotoPicker interface using platform APIs. Дополнительные сведения об этих службах зависимостей см. в разделе комплектации фотографии из библиотеки рисунков.For more information about these dependency services, see Picking a photo from the picture library.

В iOS и UWP PhotoPicker классы имеют следующий конструктор, который требует ILogger аргумент:On iOS and UWP, the PhotoPicker classes have the following constructor, which requires an ILogger argument:

public PhotoPicker(ILogger logger)
{
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

На всех платформах, осуществляется регистрация типа с помощью контейнера внедрения зависимостей RegisterTypes метод, который вызывается до загрузки приложения с платформы LoadApplication(new App()) метод.On all the platforms, type registration with the dependency injection container is performed by the RegisterTypes method, which is invoked prior to the platform loading the application with the LoadApplication(new App()) method. В следующем примере показан RegisterTypes метод для универсальной платформы Windows:The following example shows the RegisterTypes method on UWP:

void RegisterTypes()
{
    DIContainerDemo.App.RegisterType<ILogger, Logger>();
    DIContainerDemo.App.RegisterType<IPhotoPicker, Services.UWP.PhotoPicker>();
    DIContainerDemo.App.BuildContainer();
}

В этом примере Logger конкретный тип зарегистрирован с помощью сопоставления с его тип интерфейса и PhotoPicker типа также регистрируется через сопоставление интерфейса.In this example, the Logger concrete type is registered via a mapping against its interface type, and the PhotoPicker type is also registered via a interface mapping.

PhotoPicker Конструктор на платформе Android немного сложнее, так как требует Context аргумент в дополнение к ILogger аргумент:The PhotoPicker constructor on the Android platform is slightly more complicated as it requires a Context argument in addition to the ILogger argument:

public PhotoPicker(Context context, ILogger logger)
{
    _context = context ?? throw new ArgumentNullException(nameof(context));
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

В следующем примере показан RegisterTypes метод на платформе Android:The following example shows the RegisterTypes method on the Android platform:

void RegisterTypes()
{
    App.RegisterType<ILogger, Logger>();
    App.RegisterTypeWithParameters<IPhotoPicker, Services.Droid.PhotoPicker>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
    App.BuildContainer();
}

В этом примере App.RegisterTypeWithParameters регистры метод PhotoPicker с помощью контейнера внедрения зависимостей.In this example, the App.RegisterTypeWithParameters method registers the PhotoPicker with the dependency injection container. Метод регистрации гарантирует, что MainActivity экземпляр внедряются как Context аргумента и создает Logger тип внедряются как ILogger аргумент.The registration method ensures that the MainActivity instance will be injected as the Context argument, and that the Logger type will be injected as the ILogger argument.

Когда пользователь переходит на страницу выбора фотографии и выбирает фотографию, OnSelectPhotoButtonClicked выполняется обработчик:When the user navigates to the photo picking page and chooses to select a photo, the OnSelectPhotoButtonClicked handler is executed:

async void OnSelectPhotoButtonClicked(object sender, EventArgs e)
{
    ...
    var photoPickerService = DependencyService.Resolve<IPhotoPicker>();
    var stream = await photoPickerService.GetImageStreamAsync();
    if (stream != null)
    {
        image.Source = ImageSource.FromStream(() => stream);
    }
    ...
}

Когда DependencyService.Resolve<T> вызывается метод, вызывается метод разрешения зависимостей для разрешения PhotoPicker типа из контейнера внедрения зависимостей, которая также устранить и внедрить Logger типа в PhotoPicker конструктора.When the DependencyService.Resolve<T> method is invoked, the dependency resolution method will be invoked to resolve the PhotoPicker type from the dependency injection container, which will also resolve and inject the Logger type into the PhotoPicker constructor.

Примечание

Resolve<T> Метод должен использоваться при разрешении типа из контейнера внедрения зависимостей приложения с помощью DependencyService .The Resolve<T> method must be used when resolving a type from the application's dependency injection container via the DependencyService.