Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Um padrão comum que pode ser usado para aumentar a modularidade na base de código de uma aplicação usando o padrão MVVM é usar algum tipo de inversão de controlo. Uma das soluções mais comuns, em particular, é usar injeção de dependências, que consiste em criar vários serviços que são injetados em classes backend (ou seja, passados como parâmetros para os construtores do viewmodel) – isto permite que o código que utiliza estes serviços não dependa dos detalhes da implementação desses serviços, e também facilita a troca das implementações concretas desses serviços. Este padrão também facilita disponibilizar funcionalidades específicas da plataforma ao código backend, abstraindo-as através de um serviço que é depois injetado onde necessário.
O MVVM Toolkit não fornece APIs integradas para facilitar a utilização deste padrão, uma vez que já existem bibliotecas dedicadas especificamente a esse fim, como o pacote Microsoft.Extensions.DependencyInjection, que fornece um conjunto completo e robusto de APIs de DI e funciona como um IServiceProvider fácil de configurar e utilizar. O guia seguinte referir-se-á a esta biblioteca e fornecerá uma série de exemplos de como a integrar em aplicações usando o padrão MVVM.
APIs de Plataforma:
Ioc
Configurar e resolver serviços
O primeiro passo é declarar uma IServiceProvider instância e inicializar todos os serviços necessários, normalmente no arranque. Por exemplo, em UWP (mas uma configuração semelhante pode ser usada noutros frameworks também):
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 Services propriedade é inicializada no arranque, e todos os serviços de aplicação e modelos de visualização são registados. Existe também uma nova Current propriedade que pode ser usada para aceder facilmente à Services propriedade a partir de outras perspetivas na aplicação. Por exemplo:
IFilesService filesService = App.Current.Services.GetService<IFilesService>();
// Use the files service here...
O aspeto chave aqui é que cada serviço pode muito bem estar a usar APIs específicas da plataforma, mas como todas essas são abstraídas através da interface que o nosso código utiliza, não precisamos de nos preocupar com elas quando estamos apenas a resolver uma instância e a usar para realizar operações.
Injeção por construtor
Uma funcionalidade poderosa que está disponível é a "injeção via construtor", o que significa que o fornecedor de serviços de DI consegue resolver automaticamente dependências indiretas entre serviços registados ao criar instâncias do tipo 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 que implementa a interface IFileLogger e requer instâncias de IFilesService e IConsoleService. Injeção de construtores significa que o fornecedor de serviços DI irá automaticamente reunir todos os serviços necessários, da seguinte forma:
/// <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 fornecedor de serviços DI verifica automaticamente se todos os serviços necessários estão registados, depois recupera-os e invoca o construtor para o tipo de betão registado IFileLogger , para que a instância seja devolvida.
E quanto aos viewmodels?
Um fornecedor de serviços tem "service" no nome, mas pode ser usado para resolver instâncias de qualquer classe, incluindo viewmodels! Os mesmos conceitos explicados acima continuam a aplicar-se, incluindo a injeção através do construtor. Imagine que temos um tipo ContactsViewModel, que utiliza um IContactsService e uma instância de IPhoneService no seu construtor. Poderíamos ter um ConfigureServices método assim:
/// <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();
}
E depois, no nosso ContactsView, atribuiríamos o contexto dos dados da seguinte forma:
public ContactsView()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}
Mais documentação
Para mais informações sobre Microsoft.Extensions.DependencyInjection, veja aqui.
Exemplos
- Dá uma vista de olhos à aplicação de exemplo (para múltiplos frameworks de interface) para veres o MVVM Toolkit em ação.
- Também podes encontrar mais exemplos nos testes unitários.