Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Azure DevOps Services
Het ophalen van werkitems met behulp van query's is een veelvoorkomend scenario in Azure DevOps Services. In dit artikel wordt uitgelegd hoe u dit scenario programmatisch implementeert met behulp van REST API's of .NET-clientbibliotheken.
Vereisten
| Categorie | Vereisten |
|---|---|
| Azure DevOps |
-
Een organisatie - Toegang tot een project met werkitems |
| Authenticatie | Kies één van de volgende methoden: - Microsoft Entra ID-verificatie (aanbevolen voor interactieve apps) - Verificatie van service-principal (aanbevolen voor automatisering) - Verificatie van beheerde identiteit (aanbevolen voor door Azure gehoste apps) - Persoonlijk toegangstoken (voor testen) |
| Ontwikkelomgeving | Een C#-ontwikkelomgeving. U kunt Visual Studio gebruiken |
Belangrijk
Voor productietoepassingen raden we u aan Microsoft Entra ID-verificatie te gebruiken in plaats van PERSOONLIJKE toegangstokens (PAT's). PAT's zijn geschikt voor test- en ontwikkelingsscenario's. Zie de richtlijnen voor verificatie voor hulp bij het kiezen van de juiste verificatiemethode.
Verificatieopties
In dit artikel worden meerdere verificatiemethoden beschreven die geschikt zijn voor verschillende scenario's:
Microsoft Entra ID-verificatie (aanbevolen voor interactieve apps)
Gebruik Microsoft Entra ID-verificatie voor productietoepassingen met gebruikersinteractie:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.VisualStudio.Services.InteractiveClient" Version="19.225.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />
Aanbevolen voor automatisering: verificatie met Service Principal
Voor geautomatiseerde scenario's, CI/CD-pijplijnen en servertoepassingen:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />
Verificatie van beheerde identiteit (aanbevolen voor door Azure gehoste apps)
Voor toepassingen die worden uitgevoerd op Azure-services (Functions, App Service, enzovoort):
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />
Verificatie van persoonlijk toegangstoken
Voor ontwikkelings- en testscenario's:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
Voorbeelden van C#-code
In de volgende voorbeelden ziet u hoe u werkitems ophaalt met behulp van verschillende verificatiemethoden.
Voorbeeld 1: Microsoft Entra ID-verificatie (interactief)
// 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).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"]}");
}
}
}
Voorbeeld 2: Verificatie van service-principals (geautomatiseerde scenario's)
// 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">Azure AD 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).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>();
}
}
}
}
Voorbeeld 3: Verificatie van beheerde identiteit (door Azure gehoste apps)
// 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).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>();
}
}
}
}
Voorbeeld 4: Verificatie van persoonlijk toegangstoken
// 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).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>();
}
}
}
}
Voorbeelden van gebruik
Verificatie van Microsoft Entra-id gebruiken (interactief)
class Program
{
static async Task Main(string[] args)
{
var executor = new EntraIdQueryExecutor("your-organization-name");
await executor.PrintOpenBugsAsync("your-project-name");
}
}
Verificatie van service-principal gebruiken (CI/CD-scenario's)
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"]}");
}
}
}
Verificatie van beheerde identiteiten gebruiken (Azure Functions/App Service)
public class WorkItemQueryFunction
{
[FunctionName("QueryOpenBugs")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
ILogger log)
{
var executor = new ManagedIdentityQueryExecutor("your-organization-name");
var workItems = await executor.QueryOpenBugsAsync("your-project-name");
return new OkObjectResult(new {
Count = workItems.Count,
Items = workItems.Select(wi => new {
Id = wi.Id,
Title = wi.Fields["System.Title"],
State = wi.Fields["System.State"]
})
});
}
}
Gebruik maken van persoonlijk toegangstoken-verificatie (ontwikkeling/testen)
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"]}");
}
}
}
Beste praktijken
Authenticatie
- Microsoft Entra ID gebruiken voor interactieve toepassingen met gebruikersaanmelding
- Service-principal gebruiken voor geautomatiseerde scenario's, CI/CD-pijplijnen en servertoepassingen
- Beheerde identiteit gebruiken voor toepassingen die worden uitgevoerd op Azure-services (Functions, App Service, VM's)
- Vermijd persoonlijke toegangstokens in productie; alleen gebruiken voor ontwikkeling en testen
- Codeer nooit referenties in broncode; omgevingsvariabelen of Azure Key Vault gebruiken
- Referentierotatie implementeren voor langlopende toepassingen
- Zorg voor de juiste toepassingsbereiken: Voor werkitemquery's zijn gepaste leestoegangsrechten in Azure DevOps vereist.
Foutafhandeling
- Logica voor opnieuw proberen implementeren met exponentieel uitstel voor tijdelijke fouten
- Logboekfouten op de juiste wijze registreren voor foutopsporing en bewaking
- Specifieke uitzonderingen verwerken , zoals verificatiefouten en netwerktime-outs
- Annuleringstokens gebruiken voor langdurige bewerkingen
Prestatie
- Batch-werkitem ophalen bij het uitvoeren van query's op meerdere items
- Queryresultaten beperken met de TOP-component voor grote gegevenssets
- Veelgebruikte gegevens in de cache opslaan om API-aanroepen te verminderen
- De juiste velden gebruiken om de gegevensoverdracht te minimaliseren
Queryoptimalisatie
- Specifieke veldnamen gebruiken in plaats van SELECT * voor betere prestaties
- Voeg de juiste WHERE-componenten toe om resultaten op de server te filteren
- Sorteer de resultaten op de juiste wijze voor uw gebruikssituatie
- Overweeg querylimieten en paginering voor grote resultatensets
Probleemoplossingsproces
Verificatieproblemen
- Verificatiefouten met Microsoft Entra ID: zorg ervoor dat de gebruiker over de juiste machtigingen beschikt en is aangemeld bij Azure DevOps
- Verificatiefouten met service-principal: controleer of de client-id, het geheim en de tenant-id juist zijn; service-principalmachtigingen controleren in Azure DevOps
- Mislukte verificatie van beheerde identiteit: zorg ervoor dat de Azure-resource een beheerde identiteit heeft ingeschakeld en de juiste machtigingen heeft
-
PAT-verificatiefouten: controleer of het token geldig is en de juiste scopes heeft (
vso.workvoor de toegang tot werkitems) - Verloop van token: controleer of uw PAT is verlopen en genereer indien nodig een nieuwe
Problemen met query's
- Ongeldige WIQL-syntaxis: zorg ervoor dat de syntaxis van uw querytaal voor werkitems juist is
- Fouten met projectnamen: controleer of de projectnaam bestaat en juist is gespeld
-
Veldnaamfouten: gebruik de juiste systeemveldnamen (bijvoorbeeld
System.Id,System.Title)
Algemene uitzonderingen
- VssUnauthorizedException: Verificatiereferenties en -machtigingen controleren
- ArgumentException: controleer of alle vereiste parameters zijn opgegeven en geldig
- HttpRequestException: Netwerkconnectiviteit en servicebeschikbaarheid controleren
Prestatieproblemen
- Trage query's: Voeg de juiste WHERE-clausules toe en beperk de resultaatsets.
- Geheugengebruik: Grote resultatensets verwerken in batches
- Snelheidsbeperking: Logica voor opnieuw proberen implementeren met exponentieel uitstel