Condividi tramite


Ioc (inversione del controllo)

Un modello comune che può essere usato per aumentare la modularità nella codebase di un'applicazione usando il modello MVVM consiste nell'usare una forma di inversione del controllo. Una delle soluzioni più comuni in particolare consiste nell'usare l'inserimento delle dipendenze, che consiste nella creazione di un certo numero di servizi inseriti in classi back-end (ad esempio, passate come parametri ai costruttori del modello di visualizzazione), che consente al codice di usare questi servizi di non basarsi sui dettagli di implementazione di questi servizi e semplifica anche lo scambio delle implementazioni concrete di questi servizi. Questo modello semplifica anche la disponibilità di funzionalità specifiche della piattaforma per il codice back-end, astraendole tramite un servizio che viene quindi inserito dove necessario.

MVVM Toolkit non fornisce API predefinite per facilitare l'uso di questo modello, perché esistono già librerie dedicate specifiche per questo, ad esempio il Microsoft.Extensions.DependencyInjection pacchetto, che fornisce un set completo e potente di API di inserimento e funge da semplice da configurare e usare IServiceProvider. La guida seguente farà riferimento a questa libreria e fornirà una serie di esempi di come integrarlo nelle applicazioni usando il modello MVVM.

API della piattaforma:Ioc

Configurare e risolvere i servizi

Il primo passaggio consiste nel dichiarare un'istanza IServiceProvider e inizializzare tutti i servizi necessari, in genere all'avvio. Ad esempio, in UWP (ma una configurazione simile può essere usata anche in altri framework):

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();
    }
}

In questo caso la Services proprietà viene inizializzata all'avvio e vengono registrati tutti i servizi dell'applicazione e i modelli di visualizzazione. È anche disponibile una nuova Current proprietà che può essere usata per accedere facilmente alla Services proprietà da altre visualizzazioni nell'applicazione. Ad esempio:

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

// Use the files service here...

L'aspetto chiave è che ogni servizio può essere molto utile usare API specifiche della piattaforma, ma poiché queste sono tutte astratte tramite l'interfaccia usata dal codice, non è necessario preoccuparsi di esse ogni volta che si sta risolvendo un'istanza e usandola per eseguire operazioni.

Inserimento del costruttore

Una funzionalità potente disponibile è l'inserimento del costruttore, il che significa che il provider di servizi di inserimento delle dipendenze è in grado di risolvere automaticamente le dipendenze indirette tra i servizi registrati durante la creazione di istanze del tipo richiesto. Si consideri il servizio seguente:

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...
}

Qui è disponibile un FileLogger tipo che implementa l'interfaccia IFileLogger e richiede IFilesService le istanze e IConsoleService . L'inserimento del costruttore indica che il provider di servizi di inserimento delle dipendenze raccoglie automaticamente tutti i servizi necessari, come indicato di seguito:

/// <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>();

Il provider di servizi di inserimento delle dipendenze verificherà automaticamente se tutti i servizi necessari sono registrati, quindi li recupererà e richiamerà il costruttore per il tipo concreto registrato IFileLogger , per ottenere la restituzione dell'istanza.

Che ne dici dei modelli di visualizzazione?

Un provider di servizi ha "servizio" nel nome, ma può effettivamente essere usato per risolvere le istanze di qualsiasi classe, inclusi i modelli di visualizzazione. Gli stessi concetti illustrati in precedenza si applicano ancora, incluso l'inserimento del costruttore. Si supponga di avere un ContactsViewModel tipo, usando un oggetto IContactsService e un'istanza IPhoneService tramite il relativo costruttore. È possibile avere un ConfigureServices metodo simile al seguente:

/// <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();
}

In , quindi ContactsView, si assegnerebbe il contesto dati come segue:

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

Altri documenti

Per altre info su Microsoft.Extensions.DependencyInjection, vedere qui.

Esempi