Compartilhar via


Ioc (Inversão de controle)

Um padrão comum que pode ser usado para aumentar a modularidade na base de código de um aplicativo usando o padrão MVVM é usar alguma forma de inversão de controle. Uma das soluções mais comuns, em particular, é usar a injeção de dependência, que consiste na criação de vários serviços que são injetados em classes de back-end (por exemplo, passadas como parâmetros para os construtores do viewmodel). Isso permite que o código que usa esses serviços não dependa dos detalhes de implementação desses serviços e também facilita a troca de implementações concretas desses serviços. Esse padrão também facilita a disponibilização de recursos específicos da plataforma para o código de back-end, abstraindo-os por meio de um serviço que é injetado quando necessário.

O Kit de Ferramentas MVVM não fornece APIs internas para facilitar o uso desse padrão, pois já existem bibliotecas dedicadas especificamente para isso, como o pacote Microsoft.Extensions.DependencyInjection, que fornece um conjunto de DI de APIs totalmente em destaque e avançado, e atua como um IServiceProvider fácil de configurar e usar. O guia a seguir fará referência a essa biblioteca e fornecerá uma série de exemplos de como integrá-la a aplicativos usando o padrão MVVM.

APIs de Plataforma: Ioc

Configurar e resolver serviços

A primeira etapa é declarar uma instância IServiceProvider e inicializar todos os serviços necessários, geralmente na inicialização. Por exemplo, na UWP (mas uma configuração semelhante também pode ser usada em outras estruturas):

public sealed partial class App : Application
{
    public App()
    {
        Services = ConfigureServices();

        this.InitializeComponent();
    }

    /// <summary>
    /// Gets the current <see cref="App"/> instance in use
    /// </summary>
    public new static App Current => (App)Application.Current;

    /// <summary>
    /// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
    /// </summary>
    public IServiceProvider Services { get; }

    /// <summary>
    /// Configures the services for the application.
    /// </summary>
    private static IServiceProvider ConfigureServices()
    {
        var services = new ServiceCollection();

        services.AddSingleton<IFilesService, FilesService>();
        services.AddSingleton<ISettingsService, SettingsService>();
        services.AddSingleton<IClipboardService, ClipboardService>();
        services.AddSingleton<IShareService, ShareService>();
        services.AddSingleton<IEmailService, EmailService>();

        return services.BuildServiceProvider();
    }
}

Aqui, a propriedade Services é inicializada na inicialização e todos os serviços de aplicativo e viewmodels são registrados. Há também uma nova propriedade Current que pode ser usada para acessar facilmente a propriedade Services de outras exibições no aplicativo. Por exemplo:

IFilesService filesService = App.Current.Services.GetService<IFilesService>();

// Use the files service here...

O principal aspecto aqui é que cada serviço pode estar usando APIs específicas da plataforma, mas como todas elas são abstraídas por meio da interface que o código está usando, não é necessário se preocupar com elas sempre que estivermos resolvendo uma instância e usando-a para executar operações.

Injeção de construção

Um recurso poderoso que está disponível é a “injeção de construtor”, que significa que o provedor de serviços de DI é capaz de resolver automaticamente dependências indiretas entre serviços registrados ao criar instâncias do tipo que está sendo solicitado. Considere o seguinte serviço:

public class FileLogger : IFileLogger
{
    private readonly IFilesService FileService;
    private readonly IConsoleService ConsoleService;

    public FileLogger(
        IFilesService fileService,
        IConsoleService consoleService)
    {
        FileService = fileService;
        ConsoleService = consoleService;
    }

    // Methods for the IFileLogger interface here...
}

Aqui temos um tipo FileLogger implementando a interface IFileLogger e exigindo instâncias IFilesService e IConsoleService. A injeção de construtor significa que o provedor de serviços de DI reunirá automaticamente todos os serviços necessários, da seguinte maneira:

/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
    var services = new ServiceCollection();

    services.AddSingleton<IFilesService, FilesService>();
    services.AddSingleton<IConsoleService, ConsoleService>();
    services.AddSingleton<IFileLogger, FileLogger>();

    return services.BuildServiceProvider();
}

// Retrieve a logger service with constructor injection
IFileLogger fileLogger = App.Current.Services.GetService<IFileLogger>();

O provedor de serviços de DI verificará automaticamente se todos os serviços necessários estão registrados e, em seguida, os recuperará e invocará o construtor do tipo concreto IFileLogger registrado, para fazer com que a instância retorne.

E os viewmodels?

Um provedor de serviços tem “serviço” no nome, mas pode ser usado para resolver instâncias de qualquer classe, incluindo viewmodels! Os mesmos conceitos explicados acima ainda se aplicam, incluindo a injeção de construtor. Imagine que temos um tipo ContactsViewModel, usando uma instância IContactsService e outra IPhoneService por meio de seu construtor. Poderíamos ter um método ConfigureServices como este:

/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
    var services = new ServiceCollection();

    // Services
    services.AddSingleton<IContactsService, ContactsService>();
    services.AddSingleton<IPhoneService, PhoneService>();

    // Viewmodels
    services.AddTransient<ContactsViewModel>();

    return services.BuildServiceProvider();
}

Em seguida, em nosso ContactsView, atribuiremos o contexto de dados da seguinte maneira:

public ContactsView()
{
    this.InitializeComponent();
    this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}

Mais documentos

Para obter mais informações sobre Microsoft.Extensions.DependencyInjection, consulte aqui.

Exemplos

  • Confira o aplicativo de exemplo (para várias estruturas de interface do usuário) para ver o Kit de Ferramentas MVVM em ação.
  • Você também pode encontrar mais exemplos nos testes de unidade.