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 MVVM Toolkit não fornece APIs integradas para facilitar o uso desse padrão, pois já existem bibliotecas dedicadas especificamente a isso, como o pacote Microsoft.Extensions.DependencyInjection, que fornece um conjunto abrangente e robusto de APIs de DI, além de servir 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 durante a inicialização, e todos os serviços da aplicação e modelos de exibição 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 via construtor

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 quanto aos 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 via 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.