Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Azure DevOps Services
Извлечение рабочих элементов с помощью запросов — это распространенный сценарий в службах Azure DevOps. В этой статье объясняется, как реализовать этот сценарий программным способом с помощью REST API или клиентских библиотек .NET.
Подсказка
Вы можете использовать ИИ, чтобы помочь с этой задачей позже в этой статье или ознакомиться с включение помощи ИИ в Azure DevOps MCP Server, чтобы начать работу.
Предварительные условия
| Категория | Требования |
|---|---|
| Azure DevOps |
-
Организация — доступ к проекту с рабочими элементами |
| Аутентификация | Выберите для этого один из следующих методов: - Microsoft Entra ID аутентификации (рекомендуется для интерактивных приложений) - Аутентификация служебного принципала (рекомендуется для автоматизации) - Личный маркер доступа (для тестирования) |
| среды разработки | Среда разработки C#. Можно использовать Visual Studio |
Это важно
Рассмотрите возможность использования более безопасных токенов Microsoft Entra вместо более рискованных персональных токенов доступа. Дополнительные сведения см. в разделе "Сокращение использования PAT". Просмотрите рекомендации по проверке подлинности , чтобы выбрать правильный механизм проверки подлинности для ваших потребностей.
Варианты проверки подлинности
В этой статье показано несколько методов проверки подлинности для различных сценариев:
проверка подлинности Microsoft Entra ID (рекомендуется для интерактивных приложений)
Для рабочих приложений с взаимодействием с пользователем используйте проверку подлинности Microsoft Entra ID:
<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" />
Аутентификация служебного принципала (рекомендуется для автоматизации)
Для автоматизированных сценариев, конвейеров CI/CD и серверных приложений:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.67.2" />
Проверка подлинности управляемой идентичности (рекомендуется для приложений, размещенных в Azure)
Для приложений, работающих в службах Azure (функции, служба приложений и т. д.):
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
Проверка подлинности с помощью персональных токенов доступа
Для сценариев разработки и тестирования:
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.232.1" />
Примеры кода C#
В следующих примерах показано, как получить рабочие элементы с помощью различных методов проверки подлинности.
Пример 1. Проверка подлинности Microsoft Entra ID (интерактивная)
Замечание
Для использования в этом примере класса VssAadCredential требуется пакет Microsoft.VisualStudio.Services.InteractiveClient и поддержка .NET Framework. Для приложений .NET Core/.NET 5+ используйте подход на основе MSAL, показанный в Example 2 (субъект-служба) или Example 3 (управляемое удостоверение) с 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"]}");
}
}
}
Пример 2: Проверка подлинности сервисного принципала (автоматизированные сценарии)
// 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>();
}
}
}
}
Пример 3: Аутентификация управляемой идентификации (приложения, размещенные в 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>();
}
}
}
}
Пример 4. Аутентификация с использованием личного токена доступа
// 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>();
}
}
}
}
Примеры использования
В следующих примерах показано, как вызывать каждый класс проверки подлинности.
Использование аутентификации Microsoft Entra ID (интерактивной)
class Program
{
static async Task Main(string[] args)
{
var executor = new EntraIdQueryExecutor("your-organization-name");
await executor.PrintOpenBugsAsync("your-project-name");
}
}
Использование проверки подлинности учетной записи службы (сценарии 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"]}");
}
}
}
Использование проверки подлинности управляемого удостоверения (Azure Functions/служба приложений)
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;
}
}
Использование аутентификации с помощью личного токена доступа (разработка и тестирование)
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"]}");
}
}
}
Лучшие практики
Аутентификация
- Use Microsoft Entra ID для интерактивных приложений с входом пользователя
- Использование субъектов-служб для автоматизированных сценариев, конвейеров CI/CD и серверных приложений
- Используйте управляемые удостоверения для приложений, работающих в службах Azure (Функции, Службы приложений, Виртуальные машины)
- Избегайте личных маркеров доступа в рабочей среде; используется только для разработки и тестирования
- Никогда не используйте учетные данные в исходном коде напрямую; используйте переменные окружения или Azure Key Vault
- Реализуйте ротацию учетных данных для долго работающих приложений
- Обеспечьте правильные области доступа - Запросы рабочих элементов требуют соответствующих разрешений на чтение в Azure DevOps.
Обработка ошибок
- Реализовать логику повторных попыток с экспоненциальной задержкой для временных сбоев
- Записывайте ошибки надлежащим образом для отладки и мониторинга
- Обработать определенные исключения, такие как сбои аутентификации и таймауты сети
- Используйте маркеры отмены для длительных операций
Производительность
- Пакетное извлечение рабочих элементов при запросе нескольких элементов
- Ограничение результатов запроса с помощью предложения TOP для больших наборов данных
- Кэшируйте часто запрашиваемые данные для уменьшения вызовов API
- Использование соответствующих полей для минимизации передачи данных
Оптимизация запросов
- Используйте определенные имена полей вместо SELECT * для повышения производительности
- Добавьте правильные предложения WHERE для фильтрации результатов на сервере
- Упорядочение результатов соответствующим образом для вашего варианта использования
- Рассмотрите ограничения запросов и разбиение на страницы для больших результирующих наборов
Устранение неполадок
Проблемы аутентификации
- Microsoft Entra ID сбои аутентификации - Убедитесь, что у пользователя есть необходимые разрешения и выполнен вход в Azure DevOps
- Сбои аутентификации субъекта-службы — проверьте правильность идентификатора клиента, секрета и идентификатора арендатора; проверьте разрешения субъекта-службы в Azure DevOps
- Managed identity authentication failures. Убедитесь, что ресурс Azure имеет включенное управляемое удостоверение и соответствующие разрешения
-
Сбои аутентификации PAT - Убедитесь, что токен действителен и имеет соответствующие области (
vso.workдля доступа к рабочему элементу) - Срок действия маркера . Проверьте, истек ли срок действия PAT и при необходимости создайте новый.
Проблемы с запросами
- Недопустимый синтаксис WIQL . Убедитесь, что синтаксис языка запросов рабочих элементов правильно
- Ошибки имени проекта. Убедитесь, что имя проекта существует и правильно орфографировано
-
Ошибки имени поля . Используйте правильные имена системных полей (например,
System.Id,System.Title)
Распространенные исключения
- VssUnauthorizedException — проверьте учетные данные и разрешения на доступ
- ArgumentException — убедитесь, что указаны и допустимы все необходимые параметры.
- HttpRequestException — проверка доступности сетевых подключений и служб
Проблемы с производительностью
- Медленные запросы - Добавьте соответствующие предложения WHERE и ограничьте результирующие наборы
- Использование памяти . Обработка больших результирующих наборов в пакетах
- Ограничение скорости - Реализация логики повторных попыток с экспоненциальной обратной задержкой
Использование ИИ для программного запроса рабочих элементов
Если у вас есть Azure DevOps MCP Server подключен к агенту ИИ в режиме агента, можно использовать запросы естественного языка для создания кода для запроса рабочих элементов.
| задачи | Пример запроса |
|---|---|
| Создание кода запроса | Write C# code to query all active bugs assigned to me in Azure DevOps using the .NET client libraries with Microsoft Entra authentication |
| Запрос REST API | Create a REST API call to fetch work items from Azure DevOps using a WIQL query with a personal access token |
| Выполнение сохраненного запроса | Show me how to use the Azure DevOps .NET client to run a saved query and retrieve work item details including custom fields |
| Экспорт в CSV | Build a .NET app that fetches work items from Azure DevOps and exports them to CSV using managed identity authentication |
| Фильтр по пути области | Write C# code to query work items under area path <Contoso\Backend> that were modified in the last 7 days |
| Разбить на страницы большие результаты | Show me how to query Azure DevOps work items in batches of 200 using the .NET client libraries with proper pagination |
Замечание
Режим агента и сервер MCP используют естественный язык, чтобы настроить эти запросы или задать дальнейшие вопросы, чтобы уточнить результаты.