Compartilhar via


Injeção de dependência em extensões VisualStudio.Extensibility

Muitos componentes do SDK VisualStudio.Extensibility, como manipuladores de comando e provedores de janela de ferramentas, são implementados como classes individuais. Para ajudar a compartilhar componentes entre essas classes, o SDK utiliza injeção de dependência do .NET para instanciar essas classes conforme necessário. Para simplificar o compartilhamento de dados entre esses componentes, incentivamos os desenvolvedores de extensão a contribuir com seus componentes compartilhados para o grafo de injeção de dependência também.

Adicionar serviços internos ao grafo de injeção de dependência

Cada extensão no SDK visualStudio.Extensibility tem seu próprio grafo de serviço que é criado quando a extensão é carregada pela primeira vez. As extensões podem substituir o método InitializeServices para adicionar seus próprios serviços ao grafo de injeção de dependência. Você pode consultar o Markdown Linter para obter um exemplo de como usar a injeção de dependência para compartilhar um serviço.

Nota

Ao contrário do padrão de IServiceProvider do SDK do Visual Studio, esses serviços só são visíveis para outros componentes na mesma extensão e não se destinam a serem compartilhados com outras extensões.

Serviços fornecidos pelo SDK do VisualStudio.Extensibility

Além dos serviços fornecidos pela extensão, o SDK do VisualStudio.Extensibility adiciona os seguintes serviços ao grafo ao criar a instância de extensão:

  • TraceSource: uma instância de TraceSource compartilhada é adicionada ao gráfico que os componentes podem usar para registrar avisos e erros. Esses logs podem ser úteis para diagnosticar problemas com a extensão a partir de relatórios de clientes.

  • visualStudioExtensibility: instância de extensibilidade que expõe APIs para interagir com o Visual Studio, como documentos, editor, workspace.

  • IServiceBroker: o Service Broker pode ser usado para acessar serviços agenciados oferecidos pelo Visual Studio ou outros serviços que podem não fazer parte da área de superfície VisualStudioExtensibility .

  • IServiceProvider: o provedor de serviços pode ser usado para consultar serviços no grafo de injeção de dependência da própria extensão. Essa instância não é a mesma instância que o provedor de serviços global no processo do Visual Studio para extensões em processo.

Serviços adicionais para extensões em andamento

Para extensões que estão sendo executadas no processo, os seguintes serviços também estão disponíveis:

  • JoinableTaskFactory e JoinableTaskContext: ao executar como uma extensão em processo utilizando outros serviços do Visual Studio, talvez seja necessário utilizar essas instâncias para interagir com o thread principal sem causar deadlocks. Para obter mais informações, consulte Guia do Visual Studio Threading.

  • AsyncServiceProviderInjection e MefInjection: essas classes podem ser usadas para recuperar serviços em processo oferecidos pela infraestrutura IServiceProvider ou MEF em componentes de extensão VisualStudio.Extensibility. Quando disponível, recomendamos utilizar os serviços oferecidos pelo SDK do VisualStudio.Extensibility primeiro.

  • IAsyncServiceProvider2: Essa classe pode ser usada como uma alternativa a AsyncServiceProviderInjection para consultar serviços em processo do Visual Studio usando o método GetServiceAsync.

Métodos de extensão fornecidos pelo SDK do VisualStudio.Extensibility

Os métodos de extensão para a instância de IServiceCollection também podem ser usados para ajudar a adicionar serviços relacionados aos recursos de extensibilidade:

  • AddSettingsObservers: Esse método é gerado quando uma extensão contribui com uma SettingsCategory e pode ser chamado em InitializeServices para injetar serviços de observador para a categoria de configurações contribuídas. Você pode consultar SettingsSample para ver um exemplo desse método sendo usado.

Tempo de vida do serviço

Ao adicionar um novo serviço ao grafo de injeção de dependência no método InitializeServices, há três opções diferentes de ciclo de vida. Você também pode consultar o exemplo para ver como diferentes opções de ciclo de vida são usadas em uma extensão.

  • AddSingleton: esses serviços compartilham o mesmo tempo de vida que a instância de extensão. Na maioria das vezes, usar serviços de singleton é a escolha apropriada para uma extensão do SDK VisualStudio.Extensibility.

  • AddTransient: os serviços transitórios criam uma nova instância da classe de implementação ou chamam o método de fábrica sempre que um serviço o consulta. Como resultado, cada componente obtém sua própria instância do serviço.

  • AddScoped: esse escopo só se aplica a classes que têm o atributo VisualStudioContribution e os serviços consultados por elas. No contexto do SDK visualStudio.Extensibility, um escopo é definido pelo tempo de vida de um componente contribuido. Na maioria dos casos, esse tempo de vida é o mesmo que o tempo de vida da extensão, portanto, serviços com escopo versus singleton se comportarão da mesma forma. A documentação fará uma observação especial se houver componentes contribuídos com diferentes ciclos de vida.

Exemplo de caso de uso

Este exemplo demonstra como usar um objeto de origem de dados compartilhado entre uma implementação de janela de ferramenta e um manipulador de comandos usando injeção de dependência e InitializeServices.

  • Em MyExtension.InitializeServices, MyDataSource é adicionado como um serviço singleton, pois é compartilhado entre componentes. MyToolWindowControl é adicionado como transitório porque cada instância da janela de ferramentas deve ter sua própria instância exclusiva do controle hospedado.

  • Em MyToolWindow, injetamos IServiceProvider no construtor para evitar a inicialização antecipada de MyToolWindowControl e, em vez disso, consultá-lo por meio de GetRequiredService quando necessário.

[VisualStudioContribution]
public class MyExtension : Extension
{
    protected override void InitializeServices(IServiceCollection serviceCollection)
    {
        // Always make sure to call the base method to add required services.
        base.InitializeServices(serviceCollection);

        serviceCollection.AddSingleton<MyDataSource>();
        serviceCollection.AddTransient<MyToolWindowControl>();
    }
}

[DataContract]
public class MyDataSource : NotifyPropertyChangedObject
{
}

public class MyToolWindowControl : RemoteUserControl
{
    public MyToolWindowControl(MyDataSource dataSource) : base(dataContext)
    {
    }
}

[VisualStudioContribution]
public class MyToolWindow : ToolWindow
{
    private readonly IServiceProvider serviceProvider;

    public MyToolWindow(IServiceProvider serviceProvider)
    {
    }

    public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
    {
        var control = this.serviceProvider.GetRequiredService<MyToolWindowControl>();
        return Task.FromResult<IRemoteUserControl>(control);
    }
}

[VisualStudioContribution]
public class MyCommand : Command
{
    public MyCommand(MyDataSource dataSource) { }
}