Partilhar via


Consultar a API de projetos

A API de consulta de projetos VisualStudio.Extensibility permite consultar informações no sistema de projetos. Os sistemas de projetos são um dos componentes do Visual Studio para ajudar os usuários a trabalhar e manter projetos, executar compilações para produzir resultados e testar a saída.

O objetivo da API de consulta de projetos é:

  1. Trabalhar com sistemas de projetos
  2. Recuperar dados de projetos
  3. Fazer alterações em projetos

Alguns exemplos incluem entender os arquivos incluídos em um projeto, os pacotes NuGet referenciados por um projeto, adicionar novos arquivos a um projeto ou alterar as propriedades de um projeto.

Encontre mais informações sobre sistemas de projetos aqui. Encontre documentação conceitual sobre o que é o sistema de projetos, seus usos e seus vários termos aqui.

Trabalhar com a API de consulta de projetos

Esta visão geral aborda os principais cenários de trabalho com a API de consulta de projetos:

Acessar o espaço de consulta de projetos

Antes de consultar o sistema de projetos, você precisa obter uma instância do objeto espaço de consulta de projeto, que tem vários métodos assíncronos que consultam ou atualizam o sistema de projetos. O termo espaço de consulta de projetos e o termo espaço de trabalho significam a mesma coisa, o objeto que fornece acesso a todos os dados de um projeto.

Acesso ao espaço de consulta de projetos em uma extensão fora do processo

Se você estiver criando uma extensão fora do processo, use o seguinte código:

WorkspacesExtensibility workSpace = this.Extensibility.Workspaces();

Acesso ao espaço de consulta de projetos em uma extensão dentro do processo

Se você estiver criando uma extensão dentro do processo, acesse o espaço de consulta de projetos, conforme mostrado no exemplo de código a seguir. A menos que você tenha criado especificamente uma extensão dentro do processo, use o trecho na seção anterior para obter uma instância do objeto espaço de consulta de projetos.

No trecho de código a seguir, package representa uma instância de AsyncPackage, uma classe utilizada no desenvolvimento de extensões do Visual Studio. O método GetServiceAsync é empregado para obter de modo assíncrono, o serviço de consulta do contêiner de serviço do Visual Studio.

IProjectSystemQueryService queryService = await package.GetServiceAsync<IProjectSystemQueryService, IProjectSystemQueryService>();
ProjectQueryableSpace workSpace = queryService.QueryableSpace;

Consultar o sistema de projetos de um projeto

O objeto WorkspacesExtensibility permite que você consulte um projeto individual, se tiver o GUID do projeto. Geralmente, há dois GUIDs, associados a um projeto, um que representa o tipo de projeto e outro que representa o projeto de modo exclusivo. Você pode encontrar o GUID exclusivo do projeto no arquivo da solução ou, em uma extensão, pode consultar a propriedade Guid conforme demonstrado na próxima seção.

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projectList = workspace
    .ProjectsByProjectGuid(knownGuid) 
    .QueryAsync(cancellationToken);

Especificar os parâmetros do projeto a serem incluídos nos resultados da consulta

Ao consultar o sistema de projetos, você pode usar as cláusulas With para controlar quais parâmetros ou metadados serão incluídos nos resultados da consulta. Há várias maneiras válidas de especificar quais parâmetros devem ser incluídos.

Exemplo usando uma cláusula With separada para cada parâmetro

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> allProjects = workSpace
    .Projects
    .With(p => p.Path)
    .With(p => p.Guid)
    .With(p => p.Kind)      // DTE.Project.Kind
    .With(p => p.Type)      // VSHPROPID_ProjectType
    .With(p => p.TypeGuid)  // VSHPROPID_TypeGuid
    .With(p => p.Capabilities)
    .QueryAsync(cancellationToken);

    await foreach (IQueryResultItem<IProjectSnapshot> project in allProjects)
    {
        var projectGuid = project.Value.Guid;
        // Checking whether 'Capabilities' property has been retrieved.
        // Otherwise, it can throw for projects which do not support it. (Like SQL projects)
        bool capabilities = project.Value.PropertiesAvailableStatus.Capabilities;
    }

Exemplo usando uma única cláusula With para especificar vários parâmetros

Você também pode especificar os vários parâmetros desejados em uma única cláusulaWith.

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> allProjects = workSpace
    .Projects
    .With(p => new { p.Path, p.Guid, p.Capabilities })
    .QueryAsync(cancellationToken);

Exemplo usando uma cláusula WithRequired

Quando se usa WithRequired, somente os projetos com as propriedades necessárias são retornados.

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projectWithFiles = workSpace
    .Projects
    .With(p => new { p.Path, p.Guid })
    .WithRequired(p => p.Files.Where(f => f.FileName == "information.txt"))
    .QueryAsync(cancellationToken);

Exemplo de quando nenhuma propriedade é especificada

Quando nenhuma propriedade é especificada, o conjunto de propriedades padrão é retornado.

IAsyncEnumerable<IQueryResultItem<IPropertySnapshot>> properties = myproject
    .PropertiesByName("RootNamespace", "AssemblyVersion")
    .QueryAsync(cancellationToken);

Filtrar os resultados da consulta

Para limitar os resultados de uma consulta, há duas maneiras de aplicar a filtragem condicional: as instruções Where e os métodos de consulta com filtragem embutida.

Exemplo usando uma cláusula Where

Diferentes tipos de projeto são compatíveis com diferentes conjuntos de recursos. Com uma cláusulaWhere, você pode filtrar os projetos que são compatíveis com recursos específicos. Pode haver falha nas consultas se você não usar o filtro para projetos compatíveis com os recursos relevantes.

O código a seguir retorna o Path e Guid de todos os projetos de Web do .NET Core no espaço de trabalho:

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> webProjects = workspace
    .Projects
    .Where(p => p.Capabilities.Contains("DotNetCoreWeb"))
    .With(p => new { p.Path, p.Guid })
    .QueryAsync(cancellationToken);

Exemplo usando filtragem de RuleResultsByRuleName

No nível de projetos individuais, cada projeto possui um atributo RulesResults, que inclui um RuleName e Items. A chamada de API RuleResultsByRuleName pode ser usada para filtrar por tipo de regra dentro de um projeto.

    var results = await querySpace
        .Projects
        .With(p => p.Path)
        .With(p => p.ActiveConfigurations
            .With(c => c.RuleResultsByRuleName("CompilerCommandLineArgs")
                .With(r => r.RuleName)
                .With(r => r.Items
                    .With(i => i.Name))))
        .ExecuteQueryAsync();

Exemplo usando filtragem de ProjectsByCapabilities

Você também pode usar métodos de consulta como ProjectsByCapabilities que têm a filtragem embutida na consulta.

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> webProjects = workspace
        .ProjectsByCapabilities("DotNetCoreWeb | DotNetCoreRazor")
        .With(p => new { p.Path, p.Guid })
        .QueryAsync(cancellationToken);

Usar consultas aninhadas para especificar as propriedades desejadas

Alguns parâmetros são eles próprios conjuntos, e você pode usar consultas aninhadas para fazer especificação e filtragem similares nesses subconjuntos filhos.

Exemplo

No exemplo a seguir, uma consulta aninhada permite filtrar e especificar o conjunto de arquivos a serem incluídos em cada projeto retornado pela consulta superior.

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workspace
    .ProjectsByCapabilities("CPS")
    .With(p => new { p.Path, p.IsProjectFileSearchable })
    .With(p => p.PropertiesByName("ApplicationIcon")) // Retrieve a single property, if it exists
    .With(p => p.Files // Without any condition, retrieve all files in the project, but filter them
        .Where(f => f.Extension == ".ico")
        .With(f => new { f.Path, f.IsHidden }))
    .QueryAsync(cancellationToken);

    await foreach (IQueryResultItem<IProjectSnapshot> project in projects)
    {
        IPropertySnapshot property = project.Value.Properties.FirstOrDefault();
        string? applicationIcon = (string?)property?.Value;

        foreach (var iconFile in project.Value.Files)
        {
            string filePath = iconFile.Path;
            bool isHidden = iconFile.IsHidden;
        }
    }

Recuperar um conjunto filho usando o método Get

O modelo de projeto do Visual Studio tem conjuntos para os projetos, e também e conjuntos secundários, como arquivos ou recursos do projeto, dentro dos projetos. Para recuperar um conjunto secundário em si, você pode usar uma cláusulaGet. Como outros tipos de consulta, a cláusula Get permite que você use outras cláusulas, como a cláusulaWith, para moldar ou limitar os resultados.

IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> files = workspace
    .Projects
    .Where(p => p.Guid == knownGuid)
    .Get(p => p.Files
        .With(f => new { f.Path, f.IsHidden, f.IsSearchable }))
    .QueryAsync(cancellationToken);

    await foreach (var file in files)
    {
        string filePath = file.Value.Path;
    }

Consultar informações adicionais a partir de um item retornado anteriormente

Você pode usar os resultados de uma consulta anterior como base para consultas adicionais.

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> allProjects = workSpace
    .Projects
    .With(p => p.Path)
    .With(p => p.Guid)
    .QueryAsync(cancellationToken);

await foreach (IQueryResultItem<IProjectSnapshot> project in allProjects)
{
    // Gets child collections
    IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> files = project.Value
        .Files
        .With(f => new { f.Path, f.ItemType })
        .QueryAsync(cancellationToken);
}

Modificar um projeto

Os resultados das consultas normalmente são imutáveis. Você também pode usar a API de consultas para fazer alterações usando a cláusula AsUpdatable para acessar versões mutáveis dos resultados das consultas, e poder fazer alterações nos projetos e em itens dos projetos.

Exemplo de adição de um arquivo a um projeto nos resultados de uma consulta

IQueryResult<IProjectSnapshot> updatedProjects = workSpace
    .ProjectsByProjectGuid(knownGuid)
    .AsUpdatable()
    .CreateFile("AdditionalInformation.txt", textContent)
    .ExecuteAsync(cancellationToken);

Exemplo de adição de um arquivo a um projeto retornado anteriormente

IQueryResult<IProjectSnapshot> updatedProjects = myproject
    .AsUpdatable()
    .CreateFile("AdditionalInformation2.txt", textContent)
    .ExecuteAsync(cancellationToken);

Exemplo de renomeação de um projeto

IQueryResult<IProjectSnapshot> updatedProjects = myproject
    .AsUpdatable()
    .Rename("NewProjectName", textContent)
    .ExecuteAsync(cancellationToken);

Consultar as propriedades de um projeto

Você pode usar uma cláusula Get para consultar as propriedades de um projeto. A consulta a seguir retorna um conjunto de IPropertySnapshot que contém entradas para as duas propriedades solicitadas. IPropertySnapshot contém o nome, o nome de exibição e o valor em um ponto do tempo da propriedade.

// We assume that we can find the "RootNamespace" property in the result.
// However it isn't true from query API point of view.
// The query tries to retrieve items based on the condition, and if there is no such item, it will run successfully, only without returning items.
IAsyncEnumerable<IQueryResultItem<IPropertySnapshot>> properties = myProject
    .AsQueryable()  
    .Get(p => p.PropertiesByName("RootNamespace", "AssemblyVersion"))
    .QueryAsync(cancellationToken);

Consultar as soluções

Além de trabalhar com projetos, como mostrado anteriormente, você pode usar técnicas semelhantes para trabalhar com soluções.

IAsyncEnumerable<IQueryResultItem<ISolutionSnapshot>> solutions = workSpace
    .Solutions
    .With(s => new { s.Path, s.Guid, s.ActiveConfiguration, s.ActivePlatform })
    .QueryAsync(cancellationToken);

Consultar as pastas de soluções

Da mesma forma, você pode usar uma cláusula Get para consultar as pastas de soluções. A propriedade IsNested permite incluir ou excluir pastas aninhadas dos resultados. O Gerenciador de Soluções pode ter pastas aninhadas, por exemplo, padrão definir configurações ou recursos.

IAsyncEnumerable<IQueryResultItem<ISolutionFolderSnapshot>> solutionFolders = workSpace
    .Solutions
    .Get(s => s.SolutionFolders)
    .With(folder => folder.Name)
    .With(folder => folder.IsNested)
    .With(folder => folder.VisualPath) // it's a relative (virtual) path to represent how the folder is nested.
    .QueryAsync(cancellationToken);

Este exemplo obtém todas as pastas de soluções aninhadas, projetos e arquivos dentro de uma pasta de soluções (não aninhadas recursivamente):

IAsyncEnumerable<IQueryResultItem<ISolutionSnapshot>> solutionFoldersWithExtraInformation = mySolutionFolder
    .AsQueryable()
    .With(folder => folder.Files
        .With(f => f.Path))
    .With(folder => folder.Projects
        .With(p => new { p.Name, p.Guid }))
    .With(folder => folder.SolutionFolders
        .With(nested => nested.Name))
    .QueryAsync(cancellationToken);

Este exemplo obtém todas as pastas de solução aninhadas recursivamente. O VisualPath é o caminho como aparece no Gerenciador de Soluções.

string visualPath = mySolutionFolder.VisualPath;
IAsyncEnumerable<IQueryResultItem<ISolutionFolderSnapshot>> recursivelyNestedFolders = await workSpace
    .Solutions
    .Get(s => s.SolutionFolders)
    .Where(f => f.VisualPath.StartsWith(visualPath) && f.VisualPath != visualPath)
    .With(f => f.Name)
    .QueryAsync(cancellationToken);

Enumerar os arquivos de origem com informações adicionais em um projeto

Aqui está um exemplo que enumera todos os arquivos .xaml de um projeto e seu gerador de código:

IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> files =
workSpace.ProjectsByProjectGuid(knownGuid)
    .Get(p => p.Files)
    .Where(file => file.Extension == ".xaml")
    .With(file => file.Path)
    .With(file => file.PropertiesByName("Generator"))
    .QueryAsync(cancellationToken);

Outro exemplo é começar com um projeto retornado na consulta anterior:

IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> files = myProject
    .FilesEndingWith(".xaml")     // use built-in filter instead of 'Where' condition
    .With(file => file.Path)
    .With(file => file.PropertiesByName("Generator"))
    .QueryAsync(cancellationToken);

Ou obter todos os arquivos de conteúdo, que são os arquivos não compilados que são necessários em tempo de execução, como arquivos HTML e CSS.

IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> files =
    myProject.FilesWithItemTypes("Content")
        .With(file => file.Path)
        .QueryAsync(cancellationToken);

Ou enumerar todos os arquivos com uma determinada extensão, como os arquivos de esquema XML (arquivos .xsd), em todos os projetos:

IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> schemaFiles =
workSpace.Projects
    .Get(proj => proj.FilesEndingWith(".xsd"))
    .With(file => file.Path)
    .QueryAsync(cancellationToken);

    await foreach (IQueryResultItem<IFileSnapshot> fileResult in schemaFiles)
    {
        DoSomething(fileResult.Value.Path);
    }

Consultar os projetos que possuem um arquivo de origem específico

Os projetos e as pastas têm informações sobre os arquivos que eles possuem ou contêm, portanto, você pode usar uma cláusula WithRequired para consultar os projetos que incluem determinados arquivos.

Exemplo de como encontrar os projetos que possuem um determinado arquivo

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workspace
    .Projects
    .WithRequired(proj => proj.FilesByPath(myFilePath))
    .With(proj => proj.Guid)
    .QueryAsync(cancellationToken);

Exemplo de como encontrar as pastas de soluções que contêm um determinado arquivo

IAsyncEnumerable<IQueryResultItem<ISolutionFolderSnapshot>> solutionFolders = workspace
    .Solutions
    .Get(s => s.SolutionFolders)
    .WithRequired(folder => folder.FilesByPath(myFilePath))
    .With(folder => folder.Name)
    .With(folder => folder.Guid)
    .QueryAsync(cancellationToken);

Consultar as configurações do projeto e suas propriedades

Os projetos têm uma propriedadeConfigurationDimension, que você pode usar para encontrar informações de configuração do projeto. As informações de configuração de um projeto estão relacionadas às suas configurações da compilação (por exemplo, Debug e Release).

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workspace
    .Projects
    .With(p => new { p.Guid, p.Name })
    .With(p => p.Configurations
        .With(c => c.Name)
        .With(c => c.PropertiesByName("OutputPath"))
        .With(c => c.ConfigurationDimensions)) // ConfigurationDimension is essentially Name, Value pairs, both are default properties.
    .QueryAsync(cancellationToken);

    await foreach (IQueryResultItem<IProjectSnapshot> project in projects)
    {
        foreach (var configuration in project.Value.Configuration)
        {
            // ...
        }
    }

Consultar as referências entre projetos

Você também pode fazer uma consulta para encontrar os projetos que fazem referência a um determinado projeto.

Exemplo de como localizar todos os projetos referenciados pelo projeto atual

IAsyncEnumerable<IQueryResultItem<IProjectReferenceSnapshot>> projectReferences = myProject
    .ProjectReferences
    .With(r => r.ProjectGuid)
    .With(r => r.ReferencedProjectId)
    .QueryAsync(cancellationToken);

Exemplo de como localizar todos os projetos que referenciam o projeto atual

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workSpace
    .Projects
    .With(p => p.Guid)
    .WithRequired(p => p.ProjectReferences
        .Where(r => r.ProjectGuid == knownGuid))
    .QueryAsync(cancellationToken);

Consultar as referências a pacotes

De modo semelhante, você consultar as referências a pacotes NuGet.

Exemplo de como encontrar todos os pacotes referenciados pelo projeto atual

IAsyncEnumerable<IQueryResultItem<IProjectConfigurationSnapshot>> configurationsWithPackageReferences = myProject
    .ActiveConfigurations
    .With(c => c.Name)
    .With(c => c.PackageReferences
        .With(p => new { p.Name, p.Version }))
    .QueryAsync(cancellationToken);

Exemplo de como encontrar todos os projetos que fazem referência a um pacote NuGet específico

string packageName = "Newtonsoft.Json";

IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workSpace
    .Projects
    .With(p => p.Guid)
    .WithRequired(p => p.ActiveConfigurations
        .WithRequired(c => c.PackageReferences
            .Where(package => package.Name == packageName)))
    .QueryAsync(cancellationToken);

Consultar os grupos de saída de um projeto

As configurações de um projeto têm informações sobre os grupos de saída do projeto.

// From our list of active configurations, we need to get the first one in the list
IAsyncEnumerable<IQueryResultItem<IProjectConfigurationSnapshot>> configurations = myProject
    .ActiveConfigurations
    .QueryAsync(cancellationToken);

    IProjectConfigurationSnapshot myConfiguration = null;

    await foreach (IQueryResultItem<IProjectConfigurationSnapshot> config in configurations)
    {
        myConfiguration = config.Value;
        break;
    }

    // A multi-target project may have multiple active configurations
    IAsyncEnumerable<IQueryResultItem<IOutputGroupSnapshot>> outputGroups = myConfiguration
        .OutputGroupsByName("Built", "Symbols")
        .With(g => g.Name)
        .With(g => g.Outputs)
        .QueryAsync(cancellationToken);

Consulta para projetos de inicialização

A solução possui um conjunto de projetos de inicialização que podem ser executados como um executável.

// A query to get the list of startup project's name and path
var result = await this.QueryableSpace.Solutions
    .With(solution => solution.StartupProjects
        .With(startupproject => startupproject.Name)
        .With(startupproject => startupproject.Path))
    .QueryAsync(CancellationToken.None);

Ação para definir o projeto de inicialização

Usando a API de Consulta de Projetos, você também pode selecionar quais projetos serão executados. O exemplo abaixo mostra como dois caminhos de projeto podem ser definidos como projetos de inicialização.

// A query to set the startup project
var result = await this.QueryableSpace.Solutions
        .With(solution => solution.StartupProjects)
        .AsUpdatable()
        .SetStartupProjects("full\\path\\to\\project1.csproj", 
                            "full\\path\\to\\project2.csproj")
    .ExecuteAsync(CancellationToken.None);

Consultar as configurações de solução

A configuração da solução é uma coleção de projetos que são incluídos no build quando essa configuração está ativa. O exemplo abaixo mostra como consultar os nomes das configurações da solução.

var results = await this.Extensibility.Workspaces().QuerySolutionAsync(
    solution => solution.With(solution => solution.SolutionConfigurations
    .With(c => c.Name)),
    cancellationToken);

Exemplo de adição de uma configuração de solução

O método AddSolutionConfiguration usa três parâmetros:

  1. O primeiro parâmetro é o novo nome para a nova configuração da solução. Neste exemplo, a nova configuração da solução será chamada de Foo.
  2. O próximo parâmetro é a configuração na qual a nova configuração deve ser baseada. Abaixo, a nova configuração da solução é baseada na configuração da solução Debug existente.
  3. Por fim, o booleano representa se a configuração da solução deve ser propagada.
await this.Extensibility.Workspaces().UpdateSolutionAsync(
    solution => solution.Where(solution => solution.BaseName == "mySolution"),
    solution => solution.AddSolutionConfiguration("Foo", "Debug", false),
    cancellationToken);

Exemplo de exclusão de uma configuração de solução

DeleteSolutionConfiguration é uma chamada de API que remove a configuração da solução. No exemplo abaixo, a configuração da solução chamada Foo é removida.

await this.Extensibility.Workspaces().UpdateSolutionAsync(
    solution => solution.Where(solution => solution.BaseName == "mySolution"),
    solution => solution.DeleteSolutionConfiguration("Foo"),
    cancellationToken);

Consulta de ação para carregar/descarregar um projeto

Se um projeto precisar ser descarregado, você precisará especificar a solução e o caminho para o projeto a ser descarregado. O exemplo abaixo usa o método Extensibility.Workspaces().UpdateSolutionAsync para atualizar a solução e UnloadProject para descarregar o projeto.

await this.Extensibility.Workspaces().UpdateSolutionAsync(
    solution => solution.Where(solution => solution.BaseName == "MySolution"),
    solution => solution.UnloadProject("full\\path\\to\\project.csproj"),
    cancellationToken);

AsUpdatable também pode ser usado para carregar ou descarregar um projeto.

var result = await querySpace.Solutions
    .AsUpdatable()
    .LoadProject("full\\path\\to\\project.csproj")
    .ExecuteAsync();

Consulta de ação para compilar soluções/projetos

Na consulta de projeto, você também tem a capacidade de invocar ações de compilação no nível de projeto ou solução. Essas ações de compilação incluem:

  • BuildAsync
  • RebuildAsync
  • CleanAsync
  • DebugLaunchAsync
  • LaunchAsync

Compilar no nível da solução

A compilação no nível da solução compilará todos os projetos que são carregados na solução. Abaixo está um exemplo de compilação de uma solução.

var result = await querySpace.Solutions
        .BuildAsync(cancellationToken);

Compilar no nível do projeto

Ao compilar no nível do projeto, determine o projeto selecionado que você deseja compilar. No exemplo abaixo, myProject é um IProjectSnapshot que será compilado.

 var result = await myProject.BuildAsync(cancellationToken);

Consulta de ação para salvar soluções/projetos

SaveAsync pode ser usado no nível do projeto ou de solução.

Salvar no nível da solução

var result = await querySpace.Solutions
        .SaveAsync(cancellationToken);

Salvar no nível do projeto

myProject é um IProjectSnapshot dos projetos de destino a serem salvos.

 var result = await myProject.SaveAsync(cancellationToken);

Consulta de ação para controlar alterações na consulta

TrackUpdatesAsync pode ser usado no nível do projeto ou de solução. Ele é usado para controlar alterações no projeto ou na solução.

No exemplo, TrackUpdatesAsync é chamado na propriedade Files de um projeto, com um filtro de nome de arquivo aplicado. Isso significa que ele rastreará as alterações nos nomes de arquivo no projeto. A instância TrackerObserver é passada para receber notificações de alterações.

var projects = await querySpace.Projects.ExecuteQueryAsync(cancellationToken: CancellationToken.None);

var singleProject = projects.FirstOrDefault();
 
var unsubscriber = await singleProject
    .Files
    .With(f => f.FileName)
    .TrackUpdatesAsync(new TrackerObserver(), CancellationToken.None);

O TrackerObserver é uma classe privada que implementa a interface IObserver especificamente para IQueryTrackUpdates<IFileSnapshot>. Isso foi projetado para receber notificações sobre atualizações de rastreamento para instantâneos de arquivos.

private class TrackerObserver : IObserver<IQueryTrackUpdates<IFileSnapshot>>
{
    public void OnCompleted()
    {
        ...
    }
 
    public void OnError(Exception error)
    {
        ...
    }
 
    public void OnNext(IQueryTrackUpdates<IFileSnapshot> value)
    {
        ...
    }
 
    public override int GetHashCode()
    {
        ...
    }
}

Consulta de ação a ser ignorada

Skip pode ser usado para ignorar N resultados de uma consulta.

Neste exemplo de código, o primeiro resultado da consulta é ignorado. Por exemplo, se houver três projetos na solução, o primeiro resultado será ignorado e a consulta retornará os dois projetos restantes. Observação: a ordem não é garantida.

var projects = await queryableSpace.Projects
        .With(proj => proj.Name)
        .Skip(1)
       .ExecuteQueryAsync(new CancellationToken());

Próximas etapas

Para revisar palavras-chave e conceitos relacionados à API de Consulta de Projetos, consulte Conceitos da Consulta de Projetos.

Revise o código para ver uma extensão que usa a API de consulta de projetos em VSProjectQueryAPISample.