使用查询以编程方式提取工作项

Azure DevOps Services

使用查询提取工作项是 Azure DevOps Services 中的常见方案。 本文介绍如何使用 REST API 或 .NET 客户端库以编程方式实现此方案。

先决条件

类别 要求
Azure DevOps - 组织。<\br>- 个人访问令牌(PAT)。
开发环境 C# 开发环境。 可以使用 Visual Studio

重要

本文以个人访问令牌(PAT)为例,但不建议使用 PAT。 有关更安全的身份验证机制,请参阅 身份验证指南

在 Visual Studio 中创建 C# 项目

有关 Visual Studio 中的 C# 编程的信息,请参阅 Visual Studio C# 编程文档

C# 代码内容

以下任务在代码片段中发生:

  • 身份验证
    1. 使用个人访问令牌(PAT)创建凭据。
    2. 使用凭据生成客户端。
  • 获取工作项
    1. 创建要使用的查询。
    2. 检索该查询的结果。
    3. 按 ID 提取每个工作项。

C# 代码片段

// nuget: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 QueryExecutor
{
    private readonly Uri uri;
    private readonly string personalAccessToken;

    /// <summary>
    /// Initializes a new instance of the <see cref="QueryExecutor" /> class.
    /// </summary>
    /// <param name="orgName">
    /// An organization in Azure DevOps Services. If you don't have one, you can create one for free:
    /// <see href="https://go.microsoft.com/fwlink/?LinkId=307137" />.
    /// </param>
    /// <param name="personalAccessToken">
    /// A Personal Access Token, find out how to create one:
    /// <see href="/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops" />.
    /// </param>
    public QueryExecutor(string orgName, string personalAccessToken)
    {
        this.uri = new Uri("https://dev.azure.com/" + orgName);
        this.personalAccessToken = personalAccessToken;
    }

    /// <summary>
    /// Execute a WIQL (Work Item Query Language) query to return a list of open bugs.
    /// </summary>
    /// <param name="project">The name of your project within your organization.</param>
    /// <returns>A list of <see cref="WorkItem"/> objects representing all the open bugs.</returns>
    public async Task<IList<WorkItem>> QueryOpenBugs(string project)
    {
        var credentials = new VssBasicCredential(string.Empty, this.personalAccessToken);
        var wiql = new Wiql()
        {
            Query = "Select [Id] " +
                    "From WorkItems " +
                    "Where [Work Item Type] = 'Bug' " +
                    "And [System.TeamProject] = '" + project + "' " +
                    "And [System.State] <> 'Closed' " +
                    "Order By [State] Asc, [Changed Date] 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" };
                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>
    /// Execute a WIQL (Work Item Query Language) query to print a list of open bugs.
    /// </summary>
    /// <param name="project">The name of your project within your organization.</param>
    /// <returns>An async task.</returns>
    public async Task PrintOpenBugsAsync(string project)
    {
        var workItems = await this.QueryOpenBugs(project).ConfigureAwait(false);
        Console.WriteLine("Query Results: {0} items found", workItems.Count);

        foreach (var workItem in workItems)
        {
            Console.WriteLine(
                "{0}\t{1}\t{2}",
                workItem.Id,
                workItem.Fields["System.Title"],
                workItem.Fields["System.State"]);
        }
    }
}

故障排除

以编程方式使用 Azure DevOps 时,可能会遇到与查询执行、参数使用情况或方法重载相关的问题。 本部分提供有关常见问题、其原因以及如何有效地解决这些问题的指导。 通过了解这些故障排除步骤,可以确保更流畅的集成并避免运行时错误。

常见问题

  • Wiql 对象实例化不当:确保 Wiql 对象正确实例化并包含有效的查询。
  • 可选参数的用法不正确:验证是否正确传递了可选参数,尤其是在参数为 null 时。
  • 无效的查询语法:确保对象中的 Wiql 查询有效,并匹配预期格式。

RuntimeBinderException

在 Azure DevOps 中使用 QueryByWiqlAsync 方法时,可能会遇到 RuntimeBinderException。 当传递给方法的参数与其任何重载不匹配时,通常会发生此异常。 了解方法的签名并确保适当的参数用法有助于解决此问题。

错误
RuntimeBinderException:当传递给 QueryByWiqlAsync 方法的参数与该方法的任何重载不匹配时,会发生此异常。

解决方法
确保传递给方法的参数的类型正确且顺序正确。 方法签名如下所示:

public virtual Task<WorkItemQueryResult> QueryByWiqlAsync(
    Wiql wiql,
    bool? continueOnError = null,
    int? top = null,
    object userState = null,
    CancellationToken cancellationToken = default(CancellationToken));

具有正确用法的示例代码

以下代码片段演示了方法的正确用法 QueryByWiqlAsync ,确保正确定义参数:

using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using System;
using System.Threading.Tasks;
public async Task QueryWorkItemsAsync(WorkItemTrackingHttpClient client)
{
    var wiql = new Wiql()
    {
        Query = "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = 'YourProjectName'"
    };

    try
    {
        var result = await client.QueryByWiqlAsync(wiql);
        foreach (var workItem in result.WorkItems)
        {
            Console.WriteLine($"Work Item ID: {workItem.Id}");
        }
    }
    catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
}

代码说明

  • 创建对象Wiql:该Wiql对象包含用于提取工作项的查询。
  • 调用 QueryByWiqlAsync:将 Wiql 对象传递给方法。
  • 处理异常:捕获 RuntimeBinderException 并记录用于调试的错误消息。

通过遵循此方法,可以确保正确使用 QueryByWiqlAsync 该方法并避免常见问题,例如 RuntimeBinderException