Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Azure DevOps Services
Il recupero di elementi di lavoro tramite query è uno scenario comune nei servizi di Azure DevOps. Questo articolo illustra come implementare questo scenario a livello di codice usando le API REST o le librerie client .NET.
Suggerimento
È possibile usare l'intelligenza artificiale per facilitare questa attività più avanti in questo articolo, oppure vedere Abilitare l'assistenza AI con Azure DevOps MCP Server per iniziare.
Prerequisiti
| Categoria | Requisiti |
|---|---|
| Azure DevOps |
-
Un'organizzazione - Accesso a un progetto con elementi di lavoro |
| Autenticazione | Scegliere una delle seguenti modalità: - Microsoft Entra ID authentication (consigliato per le app interattive) - Autenticazione dell'entità servizio (consigliata per l'automazione) - Autenticazione dell'identità gestita (consigliata per le app ospitate Azure) - Token di accesso personale (per i test) |
| Ambiente di sviluppo | Un ambiente di sviluppo C#. È possibile usare Visual Studio |
Importante
Prendere in considerazione l'uso dei token Microsoft Entra più sicuri rispetto ai token personali di accesso ad alto rischio. Per altre informazioni, vedere Ridurre l'utilizzo di PAT. Esaminare le indicazioni per l'autenticazione per scegliere il meccanismo di autenticazione appropriato per le proprie esigenze.
Opzioni di autenticazione
Questo articolo illustra più metodi di autenticazione in base a scenari diversi:
autenticazione Microsoft Entra ID (consigliata per le app interattive)
Per le applicazioni di produzione con interazione con l'utente, usare Microsoft Entra ID per l'autenticazione.
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Microsoft.VisualStudio.Services.InteractiveClient" Version="19.232.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
Autenticazione del service principal (consigliato per l'automazione)
Per scenari automatizzati, pipeline CI/CD e applicazioni server:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
Autenticazione dell'identità gestita (consigliata per le app ospitate Azure)
Per le applicazioni in esecuzione nei servizi Azure (Funzioni, Servizio app e così via):
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
Autenticazione con token di accesso personale
Per scenari di sviluppo e test:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
Esempi di codice C#
Negli esempi seguenti viene illustrato come recuperare elementi di lavoro usando metodi di autenticazione diversi.
Esempio 1: autenticazione Microsoft Entra ID (interattiva)
Annotazioni
La classe VssAadCredential usata in questo esempio richiede il pacchetto Microsoft.VisualStudio.Services.InteractiveClient ed è destinata a .NET Framework. Per le applicazioni .NET Core/.NET 5+, usare l'metodo basato su MSAL mostrato in Example 2 (Service Principal) o Example 3 (Managed Identity) con VssOAuthAccessTokenCredential.
// NuGet packages:
// Microsoft.TeamFoundationServer.Client
// Microsoft.VisualStudio.Services.InteractiveClient
// Microsoft.Identity.Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
public class EntraIdQueryExecutor
{
private readonly Uri uri;
/// <summary>
/// Initializes a new instance using Microsoft Entra ID authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
public EntraIdQueryExecutor(string orgName)
{
this.uri = new Uri("https://dev.azure.com/" + orgName);
}
/// <summary>
/// Execute a WIQL query using Microsoft Entra ID authentication.
/// </summary>
/// <param name="project">The name of your project within your organization.</param>
/// <returns>A list of WorkItem objects representing all the open bugs.</returns>
public async Task<IList<WorkItem>> QueryOpenBugsAsync(string project)
{
// Use Microsoft Entra ID authentication
var credentials = new VssAadCredential();
var wiql = new Wiql()
{
Query = "SELECT [System.Id], [System.Title], [System.State] " +
"FROM WorkItems " +
"WHERE [Work Item Type] = 'Bug' " +
"AND [System.TeamProject] = @project " +
"AND [System.State] <> 'Closed' " +
"ORDER BY [System.State] ASC, [System.ChangedDate] DESC",
};
using (var httpClient = new WorkItemTrackingHttpClient(this.uri, new VssCredentials(credentials)))
{
try
{
var result = await httpClient.QueryByWiqlAsync(wiql, project).ConfigureAwait(false);
var ids = result.WorkItems.Select(item => item.Id).ToArray();
if (ids.Length == 0)
{
return Array.Empty<WorkItem>();
}
var fields = new[] { "System.Id", "System.Title", "System.State", "System.CreatedDate" };
return await httpClient.GetWorkItemsAsync(ids, fields, result.AsOf).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine($"Error querying work items: {ex.Message}");
return Array.Empty<WorkItem>();
}
}
}
/// <summary>
/// Print the results of the work item query.
/// </summary>
public async Task PrintOpenBugsAsync(string project)
{
var workItems = await this.QueryOpenBugsAsync(project).ConfigureAwait(false);
Console.WriteLine($"Query Results: {workItems.Count} items found");
foreach (var workItem in workItems)
{
Console.WriteLine($"{workItem.Id}\t{workItem.Fields["System.Title"]}\t{workItem.Fields["System.State"]}");
}
}
}
Esempio 2: Autenticazione dell'entità servizio (scenari automatizzati)
// NuGet packages:
// Microsoft.TeamFoundationServer.Client
// Microsoft.Identity.Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
public class ServicePrincipalQueryExecutor
{
private readonly Uri uri;
private readonly string clientId;
private readonly string clientSecret;
private readonly string tenantId;
/// <summary>
/// Initializes a new instance using Service Principal authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
/// <param name="clientId">Service principal client ID</param>
/// <param name="clientSecret">Service principal client secret</param>
/// <param name="tenantId">Microsoft Entra tenant ID</param>
public ServicePrincipalQueryExecutor(string orgName, string clientId, string clientSecret, string tenantId)
{
this.uri = new Uri($"https://dev.azure.com/{orgName}");
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tenantId = tenantId;
}
/// <summary>
/// Execute a WIQL query using Service Principal authentication.
/// </summary>
public async Task<IList<WorkItem>> QueryOpenBugsAsync(string project)
{
// Acquire token using Service Principal
var app = ConfidentialClientApplicationBuilder
.Create(this.clientId)
.WithClientSecret(this.clientSecret)
.WithAuthority($"https://login.microsoftonline.com/{this.tenantId}")
.Build();
var scopes = new[] { "https://app.vssps.visualstudio.com/.default" };
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
var credentials = new VssOAuthAccessTokenCredential(result.AccessToken);
var wiql = new Wiql()
{
Query = "SELECT [System.Id], [System.Title], [System.State] " +
"FROM WorkItems " +
"WHERE [Work Item Type] = 'Bug' " +
"AND [System.TeamProject] = @project " +
"AND [System.State] <> 'Closed' " +
"ORDER BY [System.State] ASC, [System.ChangedDate] DESC",
};
using (var httpClient = new WorkItemTrackingHttpClient(this.uri, new VssCredentials(credentials)))
{
try
{
var queryResult = await httpClient.QueryByWiqlAsync(wiql, project).ConfigureAwait(false);
var ids = queryResult.WorkItems.Select(item => item.Id).ToArray();
if (ids.Length == 0)
{
return Array.Empty<WorkItem>();
}
var fields = new[] { "System.Id", "System.Title", "System.State", "System.CreatedDate" };
return await httpClient.GetWorkItemsAsync(ids, fields, queryResult.AsOf).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine($"Error querying work items: {ex.Message}");
return Array.Empty<WorkItem>();
}
}
}
}
Esempio 3: Autenticazione dell'identità gestita (app ospitate Azure)
// NuGet packages:
// Microsoft.TeamFoundationServer.Client
// Azure.Identity
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Identity;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
public class ManagedIdentityQueryExecutor
{
private readonly Uri uri;
/// <summary>
/// Initializes a new instance using Managed Identity authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
public ManagedIdentityQueryExecutor(string orgName)
{
this.uri = new Uri($"https://dev.azure.com/{orgName}");
}
/// <summary>
/// Execute a WIQL query using Managed Identity authentication.
/// </summary>
public async Task<IList<WorkItem>> QueryOpenBugsAsync(string project)
{
// Use Managed Identity to acquire token
var credential = new DefaultAzureCredential();
var tokenRequestContext = new TokenRequestContext(new[] { "https://app.vssps.visualstudio.com/.default" });
var tokenResult = await credential.GetTokenAsync(tokenRequestContext);
var credentials = new VssOAuthAccessTokenCredential(tokenResult.Token);
var wiql = new Wiql()
{
Query = "SELECT [System.Id], [System.Title], [System.State] " +
"FROM WorkItems " +
"WHERE [Work Item Type] = 'Bug' " +
"AND [System.TeamProject] = @project " +
"AND [System.State] <> 'Closed' " +
"ORDER BY [System.State] ASC, [System.ChangedDate] DESC",
};
using (var httpClient = new WorkItemTrackingHttpClient(this.uri, new VssCredentials(credentials)))
{
try
{
var queryResult = await httpClient.QueryByWiqlAsync(wiql, project).ConfigureAwait(false);
var ids = queryResult.WorkItems.Select(item => item.Id).ToArray();
if (ids.Length == 0)
{
return Array.Empty<WorkItem>();
}
var fields = new[] { "System.Id", "System.Title", "System.State", "System.CreatedDate" };
return await httpClient.GetWorkItemsAsync(ids, fields, queryResult.AsOf).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine($"Error querying work items: {ex.Message}");
return Array.Empty<WorkItem>();
}
}
}
}
Esempio 4: Autenticazione del token di accesso personale
// NuGet package: Microsoft.TeamFoundationServer.Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
public class PatQueryExecutor
{
private readonly Uri uri;
private readonly string personalAccessToken;
/// <summary>
/// Initializes a new instance using Personal Access Token authentication.
/// </summary>
/// <param name="orgName">Your Azure DevOps organization name</param>
/// <param name="personalAccessToken">Your Personal Access Token</param>
public PatQueryExecutor(string orgName, string personalAccessToken)
{
this.uri = new Uri("https://dev.azure.com/" + orgName);
this.personalAccessToken = personalAccessToken;
}
/// <summary>
/// Execute a WIQL query using Personal Access Token authentication.
/// </summary>
/// <param name="project">The name of your project within your organization.</param>
/// <returns>A list of WorkItem objects representing all the open bugs.</returns>
public async Task<IList<WorkItem>> QueryOpenBugsAsync(string project)
{
var credentials = new VssBasicCredential(string.Empty, this.personalAccessToken);
var wiql = new Wiql()
{
Query = "SELECT [System.Id], [System.Title], [System.State] " +
"FROM WorkItems " +
"WHERE [Work Item Type] = 'Bug' " +
"AND [System.TeamProject] = @project " +
"AND [System.State] <> 'Closed' " +
"ORDER BY [System.State] ASC, [System.ChangedDate] DESC",
};
using (var httpClient = new WorkItemTrackingHttpClient(this.uri, new VssCredentials(credentials)))
{
try
{
var result = await httpClient.QueryByWiqlAsync(wiql, project).ConfigureAwait(false);
var ids = result.WorkItems.Select(item => item.Id).ToArray();
if (ids.Length == 0)
{
return Array.Empty<WorkItem>();
}
var fields = new[] { "System.Id", "System.Title", "System.State", "System.CreatedDate" };
return await httpClient.GetWorkItemsAsync(ids, fields, result.AsOf).ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine($"Error querying work items: {ex.Message}");
return Array.Empty<WorkItem>();
}
}
}
}
Esempi di utilizzo
Gli esempi seguenti illustrano come chiamare ogni classe di autenticazione.
Uso dell'autenticazione Microsoft Entra ID (interattiva)
class Program
{
static async Task Main(string[] args)
{
var executor = new EntraIdQueryExecutor("your-organization-name");
await executor.PrintOpenBugsAsync("your-project-name");
}
}
Uso dell'autenticazione dell'entità servizio (scenari CI/CD)
class Program
{
static async Task Main(string[] args)
{
// These values should come from environment variables or Azure Key Vault
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var executor = new ServicePrincipalQueryExecutor("your-organization-name", clientId, clientSecret, tenantId);
var workItems = await executor.QueryOpenBugsAsync("your-project-name");
Console.WriteLine($"Found {workItems.Count} open bugs via automation");
foreach (var item in workItems)
{
Console.WriteLine($"Bug {item.Id}: {item.Fields["System.Title"]}");
}
}
}
Uso dell'autenticazione dell'identità gestita (Azure Functions/App Service)
public class WorkItemQueryFunction
{
private readonly ILogger<WorkItemQueryFunction> _logger;
public WorkItemQueryFunction(ILogger<WorkItemQueryFunction> logger)
{
_logger = logger;
}
[Function("QueryOpenBugs")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequestData req)
{
var executor = new ManagedIdentityQueryExecutor("your-organization-name");
var workItems = await executor.QueryOpenBugsAsync("your-project-name");
var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
await response.WriteAsJsonAsync(new {
Count = workItems.Count,
Items = workItems.Select(wi => new {
Id = wi.Id,
Title = wi.Fields["System.Title"],
State = wi.Fields["System.State"]
})
});
return response;
}
}
Uso dell'autenticazione del token di accesso personale (sviluppo/test)
class Program
{
static async Task Main(string[] args)
{
var pat = Environment.GetEnvironmentVariable("AZURE_DEVOPS_PAT"); // Never hardcode PATs
var executor = new PatQueryExecutor("your-organization-name", pat);
var workItems = await executor.QueryOpenBugsAsync("your-project-name");
Console.WriteLine($"Found {workItems.Count} open bugs");
foreach (var item in workItems)
{
Console.WriteLine($"Bug {item.Id}: {item.Fields["System.Title"]}");
}
}
}
Procedure consigliate
Autenticazione
- Usare Microsoft Entra ID per applicazioni interattive con accesso utente
- Utilizzare i principali del servizio per scenari automatizzati, pipeline CI/CD e applicazioni per server
- Utilizza identità gestite per applicazioni in esecuzione su servizi Azure (Funzioni, App Service, VMs)
- Evitare token di accesso personali nell'ambiente di produzione; uso solo per lo sviluppo e il test
- Non incorporare mai le credenziali nel codice sorgente; usare variabili di ambiente o Azure Key Vault
- Implementare la rotazione delle credenziali per le applicazioni a esecuzione prolungata
- Assicurarsi di propri ambiti - Le query degli elementi di lavoro richiedono autorizzazioni di lettura appropriate in Azure DevOps
Gestione degli errori
- Implementare la logica di ripetizione dei tentativi con backoff esponenziale per gli errori temporanei
- Registrare gli errori in modo appropriato per il debug e il monitoraggio
- Gestire eccezioni specifiche , ad esempio errori di autenticazione e timeout di rete
- Usare i token di annullamento per le operazioni a esecuzione prolungata
Prestazioni
- Recupero di elementi di lavoro in batch durante l'esecuzione di query su più elementi
- Limitare i risultati delle query usando la clausola TOP per set di dati di grandi dimensioni
- Memorizzare nella cache i dati a cui si accede di frequente per ridurre le chiamate API
- Usare i campi appropriati per ridurre al minimo il trasferimento dei dati
Ottimizzazione della query
- Usare nomi di campo specifici anziché SELECT * per ottenere prestazioni migliori
- Aggiungere clausole WHERE appropriate per filtrare i risultati sul server
- Ordinare i risultati in modo appropriato per il caso d'uso
- Considerare i limiti delle query e la paginazione per set di risultati di grandi dimensioni
Risoluzione dei problemi
Problemi di autenticazione
- Microsoft Entra ID errori di autenticazione - Verificare che l'utente disponga delle autorizzazioni appropriate e che sia connesso a Azure DevOps
- Errori di autenticazione del principale del servizio - Verificare che l'ID client, il segreto e l'ID tenant siano corretti; controllare le autorizzazioni del principale del servizio in Azure DevOps
- Managed identity authentication failures - Assicurarsi che la risorsa Azure disponga di un'identità gestita abilitata e delle autorizzazioni corrette.
-
Errori di autenticazione PAT : verificare che il token sia valido e abbia ambiti appropriati (
vso.workper l'accesso agli elementi di lavoro) - Scadenza del token : controllare se il token di accesso personale è scaduto e generarne uno nuovo, se necessario
Problemi di query
- Sintassi WIQL non valida : verificare che la sintassi del linguaggio di query dell'elemento di lavoro sia corretta
- Errori del nome del progetto: verificare che il nome del progetto esista e che sia stato digitato correttamente
-
Errori di nome campo : usare i nomi di campo di sistema corretti (ad esempio,
System.Id,System.Title)
Eccezioni comuni
- VssUnauthorizedException - Controllare le credenziali e le autorizzazioni di autenticazione
- ArgumentException - Verificare che tutti i parametri obbligatori siano specificati e validi
- HttpRequestException - Controllare la connettività di rete e la disponibilità del servizio
Problemi di prestazioni
- Query "lente" - Aggiungere clausole WHERE appropriate e limitare i set di risultati
- Utilizzo della memoria - Elaborare set di risultati di grandi dimensioni in batch
- Limitazione della frequenza - Implementare la logica di ripetizione dei tentativi con backoff esponenziale
Usare l'intelligenza artificiale per eseguire query sugli elementi di lavoro programmaticamente
Se si dispone del Azure DevOps MCP Server connesso all'agente di intelligenza artificiale in modalità agente, è possibile usare i prompt del linguaggio naturale per generare codice per l'esecuzione di query sugli elementi di lavoro.
| Attività | Richiesta di esempio |
|---|---|
| Generare il codice della query | Write C# code to query all active bugs assigned to me in Azure DevOps using the .NET client libraries with Microsoft Entra authentication |
| Query dell'API REST | Create a REST API call to fetch work items from Azure DevOps using a WIQL query with a personal access token |
| Eseguire una query salvata | Show me how to use the Azure DevOps .NET client to run a saved query and retrieve work item details including custom fields |
| Esporta in CSV | Build a .NET app that fetches work items from Azure DevOps and exports them to CSV using managed identity authentication |
| Filtra in base al percorso dell'area | Write C# code to query work items under area path <Contoso\Backend> that were modified in the last 7 days |
| Impaginare risultati di grandi dimensioni | Show me how to query Azure DevOps work items in batches of 200 using the .NET client libraries with proper pagination |
Annotazioni
La modalità agente e il server MCP usano il linguaggio naturale, quindi è possibile modificare queste richieste o porre domande di completamento per perfezionare i risultati.