Eseguire query sull'API del progetto
L'API di query del progetto VisualStudio.Extensibility consente di eseguire query sulle informazioni dal sistema di progetto. I sistemi di progetto fanno parte dei componenti di Visual Studio per aiutare gli utenti a lavorare con e gestire progetti, eseguire compilazioni per produrre risultati e testare l'output.
L'obiettivo dell'API Project Query è:
- Usare i sistemi di progetto
- Recuperare dati dai progetti
- Apportare modifiche ai progetti
Alcuni esempi includono la comprensione dei file inclusi in un progetto, i pacchetti NuGet a cui fa riferimento un progetto, l'aggiunta di nuovi file a un progetto o la modifica delle proprietà del progetto.
Altre informazioni sui sistemi di progetto sono disponibili qui. Trovare la documentazione concettuale sul sistema di progetto, sull'utilizzo e sui vari termini qui.
Usare l'API di query del progetto
Questa panoramica illustra gli scenari principali per l'uso dell'API di query del progetto:
- Accedere allo spazio di query del progetto
- Eseguire una query sul sistema di progetto per un progetto
- Specificare i parametri del progetto da includere nel risultato della query
- Filtrare il risultato della query
- Usare query annidate per specificare le proprietà desiderate
- Recuperare una raccolta figlio usando il metodo Get
- Eseguire query su informazioni aggiuntive da un elemento restituito in precedenza
- Modificare un progetto
- Query per le proprietà del progetto
- Query per le soluzioni
- Query per le cartelle della soluzione
- Enumerazione dei file di origine con informazioni aggiuntive in un progetto
- Eseguire una query per i progetti proprietari di un file di origine specifico
- Eseguire una query per le configurazioni del progetto e le relative proprietà
- Query per riferimenti da progetto a progetto
- Query per i riferimenti ai pacchetti
- Query per i gruppi di output del progetto
Accedere allo spazio di query del progetto
Prima di poter eseguire query sul sistema del progetto, è necessario ottenere un'istanza dell'oggetto spazio query del progetto, che include diversi metodi asincroni che eseguono query o aggiornano il sistema di progetto. Il termine spazio di query del progetto e il termine area di lavoro significano entrambi la stessa cosa, ovvero l'oggetto che fornisce l'accesso a tutti i dati per un progetto.
Accesso allo spazio di query del progetto in un'estensione out-of-process
Se si sta creando un'estensione out-of-process, usare il codice seguente:
WorkspacesExtensibility workSpace = this.Extensibility.Workspaces();
Accesso allo spazio di query del progetto in un'estensione in-process
Se si sta creando un'estensione in-process, si accede invece allo spazio di query del progetto, come illustrato nell'esempio di codice seguente. A meno che non sia stata creata in modo specifico un'estensione in-process, usare il frammento nella sezione precedente per ottenere un'istanza dell'oggetto spazio query del progetto.
Nell'estratto di codice seguente rappresenta package
un'istanza di AsyncPackage, una classe utilizzata nello sviluppo di estensioni di Visual Studio. Il metodo GetServiceAsync
viene usato per ottenere in modo asincrono il servizio di query dal contenitore del servizio di Visual Studio.
IProjectSystemQueryService queryService = await package.GetServiceAsync<IProjectSystemQueryService, IProjectSystemQueryService>();
ProjectQueryableSpace workSpace = queryService.QueryableSpace;
Eseguire una query sul sistema di progetto per un progetto
L'oggetto WorkspacesExtensibility
consente di eseguire una query per un singolo progetto, se si dispone del GUID del progetto. Esistono in genere due GUID associati a un progetto, uno che rappresenta il tipo di progetto e un altro che rappresenta in modo univoco il progetto. È possibile trovare il GUID univoco del progetto nel file della soluzione o da un'estensione, è possibile eseguire una query per la Guid
proprietà, come illustrato nella sezione successiva.
IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projectList = workspace
.ProjectsByProjectGuid(knownGuid)
.QueryAsync(cancellationToken);
Specificare i parametri del progetto da includere nel risultato della query
Quando si esegue una query sul sistema di progetto, è possibile usare With
le clausole per controllare quali parametri o metadati sono inclusi nei risultati della query. Esistono diversi modi validi per specificare quali parametri devono essere inclusi.
Esempio di utilizzo di una clausola separata With
per ogni parametro
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;
}
Esempio che usa una singola With
clausola per specificare più parametri
È anche possibile specificare più parametri desiderati in una singola With
clausola.
IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> allProjects = workSpace
.Projects
.With(p => new { p.Path, p.Guid, p.Capabilities })
.QueryAsync(cancellationToken);
Esempio di utilizzo di una WithRequired
clausola
Quando si usa WithRequired
, vengono restituiti solo i progetti con le proprietà necessarie.
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);
Esempio quando non vengono specificate proprietà
Quando non vengono specificate proprietà, viene restituito il set predefinito di proprietà restituite.
IAsyncEnumerable<IQueryResultItem<IPropertySnapshot>> properties = myproject
.PropertiesByName("RootNamespace", "AssemblyVersion")
.QueryAsync(cancellationToken);
Filtrare il risultato della query
Per limitare i risultati delle query, esistono due modi per applicare il filtro condizionale: Where
istruzioni e metodi di query con filtri predefiniti.
Esempio di utilizzo di una Where
clausola
Diversi tipi di progetto supportano diversi set di funzionalità. Con una Where
clausola è possibile filtrare i progetti che supportano funzionalità specifiche. Le query possono avere esito negativo se non si filtrano i progetti che supportano le funzionalità pertinenti.
Il codice seguente restituisce e Path
Guid
di tutti i progetti Web .NET Core nell'area di lavoro:
IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> webProjects = workspace
.Projects
.Where(p => p.Capabilities.Contains("DotNetCoreWeb"))
.With(p => new { p.Path, p.Guid })
.QueryAsync(cancellationToken);
Esempio di utilizzo del filtro predefinito
È anche possibile usare metodi di query come ProjectsByCapabilities
che dispongono di filtri incorporati nella query.
IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> webProjects = workspace
.ProjectsByCapabilities("DotNetCoreWeb | DotNetCoreRazor")
.With(p => new { p.Path, p.Guid })
.QueryAsync(cancellationToken);
Usare query annidate per specificare le proprietà desiderate
Alcuni parametri sono raccolte e è possibile usare query annidate per eseguire specifiche e filtri simili per tali raccolte figlio.
Esempio
Nell'esempio seguente una query nidificata consente di filtrare e specificare la raccolta di file da includere in ogni progetto restituito dalla query esterna.
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;
}
}
Recuperare una raccolta figlio usando il metodo Get
Il modello di progetto di Visual Studio include raccolte per progetti e raccolte figlio, ad esempio per file o funzionalità di progetto all'interno dei progetti. Per recuperare una raccolta figlio stessa, è possibile utilizzare una Get
clausola . Analogamente ad altri tipi di query, la Get
clausola consente di usare altre clausole, ad esempio la With
clausola per modellare o limitare i risultati.
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;
}
Eseguire query su informazioni aggiuntive da un elemento restituito in precedenza
È possibile usare i risultati di una query precedente come base per query aggiuntive.
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);
}
Modificare un progetto
I risultati della query sono in genere non modificabili. È anche possibile usare l'API di query per apportare modifiche usando la AsUpdatable
clausola per accedere alle versioni modificabili dei risultati della query, in modo da poter apportare modifiche ai progetti e agli elementi del progetto.
Esempio di aggiunta di un file a un progetto in un risultato della query
IQueryResult<IProjectSnapshot> updatedProjects = workSpace
.ProjectsByProjectGuid(knownGuid)
.AsUpdatable()
.CreateFile("AdditionalInformation.txt", textContent)
.ExecuteAsync(cancellationToken);
Esempio di aggiunta di un file a un progetto restituito in precedenza
IQueryResult<IProjectSnapshot> updatedProjects = myproject
.AsUpdatable()
.CreateFile("AdditionalInformation2.txt", textContent)
.ExecuteAsync(cancellationToken);
Query per le proprietà del progetto
È possibile usare una Get
clausola per eseguire una query per le proprietà del progetto. La query seguente restituisce una raccolta di IPropertySnapshot
che contiene voci per le due proprietà richieste. IPropertySnapshot
contiene il nome della proprietà, il nome visualizzato e il valore in un momento specifico.
// 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);
Query per le soluzioni
Oltre a lavorare con i progetti come illustrato in precedenza, è possibile usare tecniche simili per lavorare con le soluzioni.
IAsyncEnumerable<IQueryResultItem<ISolutionSnapshot>> solutions = workSpace
.Solutions
.With(s => new { s.Path, s.Guid, s.ActiveConfiguration, s.ActivePlatform })
.QueryAsync(cancellationToken);
Query per le cartelle della soluzione
Analogamente, è possibile usare una Get
clausola per eseguire query per le cartelle della soluzione. La IsNested
proprietà consente di includere o escludere cartelle annidate dai risultati. Esplora soluzioni possono avere cartelle annidate, ad esempio per l'impostazione di configurazione o le risorse.
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);
In questo caso vengono recuperate tutte le cartelle di soluzioni annidate, i progetti, i file all'interno di una cartella della soluzione (non annidati in modo ricorsivo):
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);
In questo caso vengono recuperate tutte le cartelle di soluzioni nidificate in modo ricorsivo. VisualPath
è il percorso visualizzato in Esplora soluzioni.
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);
Enumerazione dei file di origine con informazioni aggiuntive in un progetto
Ecco un esempio di enumerazione di tutti i file xaml in un progetto e del relativo generatore di codice:
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);
Un altro esempio consiste nell'iniziare con un progetto restituito dalla query precedente:
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);
Oppure per ottenere tutti i file di contenuto, che sono file non compilati necessari in fase di esecuzione, ad esempio file HTML e CSS.
IAsyncEnumerable<IQueryResultItem<IFileSnapshot>> files =
myProject.FilesWithItemTypes("Content")
.With(file => file.Path)
.QueryAsync(cancellationToken);
In alternativa, per enumerare tutti i file con una determinata estensione, ad esempio file XML Schema (.xsd
file) in tutti i progetti:
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);
}
Eseguire una query per i progetti proprietari di un file di origine specifico
I progetti e le cartelle contengono informazioni sui file che possiedono o contengono, quindi è possibile usare una WithRequired
clausola per eseguire una query per i progetti che includono determinati file.
Esempio di ricerca di progetti proprietari di un determinato file
IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workspace
.Projects
.WithRequired(proj => proj.FilesByPath(myFilePath))
.With(proj => proj.Guid)
.QueryAsync(cancellationToken);
Esempio di ricerca di cartelle di soluzioni che contengono un determinato file
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);
Eseguire una query per le configurazioni del progetto e le relative proprietà
I progetti hanno una ConfigurationDimension
proprietà che è possibile usare per trovare le informazioni di configurazione del progetto. Le informazioni di configurazione del progetto sono correlate alle configurazioni di compilazione del progetto ,ad esempio 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)
{
// ...
}
}
Query per riferimenti da progetto a progetto
È anche possibile eseguire query per trovare progetti che fanno riferimento a un determinato progetto.
Esempio di ricerca di tutti i progetti a cui fa riferimento il progetto corrente
IAsyncEnumerable<IQueryResultItem<IProjectReferenceSnapshot>> projectReferences = myProject
.ProjectReferences
.With(r => r.ProjectGuid)
.With(r => r.ReferencedProjectId)
.QueryAsync(cancellationToken);
Esempio di ricerca di tutti i progetti che fanno riferimento al progetto corrente
IAsyncEnumerable<IQueryResultItem<IProjectSnapshot>> projects = workSpace
.Projects
.With(p => p.Guid)
.WithRequired(p => p.ProjectReferences
.Where(r => r.ProjectGuid == knownGuid))
.QueryAsync(cancellationToken);
Query per i riferimenti ai pacchetti
Analogamente, è possibile eseguire query per i riferimenti al pacchetto NuGet.
Esempio di ricerca di tutti i pacchetti a cui fa riferimento il progetto corrente
IAsyncEnumerable<IQueryResultItem<IProjectConfigurationSnapshot>> configurationsWithPackageReferences = myProject
.ActiveConfigurations
.With(c => c.Name)
.With(c => c.PackageReferences
.With(p => new { p.Name, p.Version }))
.QueryAsync(cancellationToken);
Esempio di ricerca di tutti i progetti che fanno riferimento a un pacchetto NuGet specifico
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);
Query per i gruppi di output del progetto
Le configurazioni del progetto contengono informazioni sui gruppi di output del progetto.
// 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);
Passaggi successivi
Esaminare il codice per un'estensione che usa l'API Query di progetto in VSProjectQueryAPISample.