Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Azure DevOps-tjänster
Att hämta arbetsobjekt med hjälp av frågor är ett vanligt scenario i Azure DevOps Services. Den här artikeln beskriver hur du implementerar det här scenariot programmatiskt med hjälp av REST-API:er eller .NET-klientbibliotek.
Förutsättningar
| Kategori | Krav |
|---|---|
| Azure DevOps |
-
En organisation – Åtkomst till ett projekt med arbetsobjekt |
| autentisering | Använd någon av följande metoder: - Microsoft Entra ID-autentisering (rekommenderas för interaktiva appar) - Autentisering med tjänstens huvudnamn (rekommenderas för automatisering) - Hanterad identitetsautentisering (rekommenderas för Azure-värdbaserade appar) - Personlig åtkomsttoken (för testning) |
| Utvecklingsmiljö | En C#-utvecklingsmiljö. Du kan använda Visual Studio |
Viktigt!
För produktionsprogram rekommenderar vi att du använder Microsoft Entra ID-autentisering i stället för personliga åtkomsttoken (PAT). PAT:er är lämpliga för testnings- och utvecklingsscenarier. Vägledning om hur du väljer rätt autentiseringsmetod finns i Autentiseringsvägledning.
Autentiseringsalternativ
Den här artikeln visar flera autentiseringsmetoder som passar olika scenarier:
Microsoft Entra ID-autentisering (rekommenderas för interaktiva appar)
För produktionsprogram med användarinteraktion använder du Microsoft Entra ID-autentisering:
<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" />
Autentisering med tjänstens huvudnamn (rekommenderas för automatisering)
För automatiserade scenarier, CI/CD-pipelines och serverprogram:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />
Hanterad identitetsautentisering (rekommenderas för Azure-värdbaserade appar)
För program som körs på Azure-tjänster (Functions, App Service osv.):
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />
Autentisering med personlig åtkomsttoken
För utvecklings- och testscenarier:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
C#-kodexempel
I följande exempel visas hur du hämtar arbetsobjekt med olika autentiseringsmetoder.
Exempel 1: Microsoft Entra ID-autentisering (interaktiv)
// 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"]}");
}
}
}
Exempel 2: Autentisering med tjänstens huvudnamn (automatiserade scenarier)
// 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>();
}
}
}
}
Exempel 3: Hanterad identitetsautentisering (Azure-värdbaserade appar)
// 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>();
}
}
}
}
Exempel 4: Autentisering med personlig åtkomsttoken
// 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>();
}
}
}
}
Användningsexempel
Använda Microsoft Entra ID-autentisering (interaktiv)
class Program
{
static async Task Main(string[] args)
{
var executor = new EntraIdQueryExecutor("your-organization-name");
await executor.PrintOpenBugsAsync("your-project-name");
}
}
Använda autentisering med tjänstens huvudnamn (CI/CD-scenarier)
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"]}");
}
}
}
Använda hanterad identitetsautentisering (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"]
})
});
}
}
Använda autentisering med personlig åtkomsttoken (utveckling/testning)
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"]}");
}
}
}
Metodtips
Autentisering
- Använda Microsoft Entra-ID för interaktiva program med användarinloggning
- Använda tjänstens huvudnamn för automatiserade scenarier, CI/CD-pipelines och serverprogram
- Använda hanterad identitet för program som körs på Azure-tjänster (Functions, App Service, virtuella datorer)
- Undvik personliga åtkomsttoken i produktion. endast för utveckling och testning
- Hårdkoda aldrig autentiseringsuppgifter i källkoden. använda miljövariabler eller Azure Key Vault
- Implementera rotation av autentiseringsuppgifter för långvariga program
- Se till att det finns rätt omfång: Frågor om arbetsobjekt kräver lämpliga läsbehörigheter i Azure DevOps
Felhantering
- Implementera logik för återförsök med exponentiell backoff för tillfälliga fel
- Logga fel på ett lämpligt sätt för felsökning och övervakning
- Hantera specifika undantag som autentiseringsfel och tidsgränser för nätverk
- Använda annulleringstoken för långvariga åtgärder
Prestanda
- Hämtning av Batch-arbetsobjekt vid frågor mot flera objekt
- Begränsa frågeresultat med hjälp av TOP-sats för stora datamängder
- Cachelagrade data som används ofta för att minska API-anrop
- Använd lämpliga fält för att minimera dataöverföringen
Frågeoptimering
- Använd specifika fältnamn i stället för SELECT * för bättre prestanda
- Lägga till rätt WHERE-satser för att filtrera resultat på servern
- Ordna resultaten på rätt sätt för ditt användningsfall
- Överväg frågebegränsningar och paginering för stora resultatuppsättningar
Felsökning
Autentiseringsproblem
- Microsoft Entra ID-autentiseringsfel: Kontrollera att användaren har rätt behörigheter och är inloggad på Azure DevOps
- Autentiseringsfel för tjänstens huvudnamn: Kontrollera att klient-ID, hemlighets- och klientorganisations-ID är korrekta. kontrollera behörigheter för tjänstens huvudnamn i Azure DevOps
- Autentiseringsfel för hanterad identitet: Kontrollera att Azure-resursen har en aktiverad hanterad identitet och rätt behörigheter
-
PAT-autentiseringsfel: Kontrollera att token är giltig och har lämpliga omfång (
vso.workför åtkomst till arbetsobjekt) - Förfallodatum för token: Kontrollera om din PAT har upphört att gälla och generera en ny om det behövs
Frågor om problem
- Ogiltig WIQL-syntax: Kontrollera att syntaxen för arbetsobjektets frågespråk är korrekt
- Projektnamnsfel: Kontrollera att projektnamnet finns och är rättstavat
-
Fältnamnsfel: Använd rätt systemfältnamn (till exempel
System.Id,System.Title)
Vanliga undantag
- VssUnauthorizedException: Kontrollera autentiseringsuppgifter och behörigheter
- ArgumentException: Kontrollera att alla obligatoriska parametrar har angetts och är giltiga
- HttpRequestException: Kontrollera nätverksanslutningen och tjänstens tillgänglighet
Prestandaproblem
- Långsamma frågor: Lägg till lämpliga WHERE-satser och begränsa resultatuppsättningar
- Minnesanvändning: Bearbeta stora resultatuppsättningar i batchar
- Hastighetsbegränsning: Implementera logik för återförsök med exponentiell backoff