Megosztás:


Munkaelemek beolvasása lekérdezésekkel programozott módon

Azure DevOps Services

A munkaelemek lekérdezésekkel való lekérése gyakori forgatókönyv az Azure DevOps Servicesben. Ez a cikk bemutatja, hogyan implementálhatja ezt a forgatókönyvet programozott módon REST API-k vagy .NET-ügyfélkódtárak használatával.

Előfeltételek

Kategória Követelmények
Azure DevOps - Egy szervezet
– Hozzáférés munkahelyi elemeket tartalmazó projekthez
hitelesítés Válasszon egyet a következő módszerek közül:
- Microsoft Entra ID-hitelesítés (interaktív alkalmazásokhoz ajánlott)
- Szolgáltatói Principal hitelesítés (automatizáláshoz ajánlott)
- Felügyelt identitás hitelesítése (az Azure által üzemeltetett alkalmazásokhoz ajánlott)
- Személyes hozzáférési jogkivonat (teszteléshez)
fejlesztési környezet C#-fejlesztési környezet. Használhatja a Visual Studiót

Fontos

Éles alkalmazások esetében a Személyes hozzáférési jogkivonatok (PAT-k) helyett a Microsoft Entra ID-hitelesítés használatát javasoljuk. A PAT-k tesztelési és fejlesztési forgatókönyvekhez alkalmasak. A megfelelő hitelesítési módszer kiválasztásával kapcsolatos útmutatásért tekintse meg a hitelesítési útmutatót.

Hitelesítési lehetőségek

Ez a cikk több hitelesítési módszert mutat be a különböző forgatókönyvek szerint:

Felhasználói beavatkozással rendelkező éles alkalmazások esetén használja a Microsoft Entra ID-hitelesítést:

<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" />

Automatizált forgatókönyvek, CI-/CD-folyamatok és kiszolgálóalkalmazások esetén:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.61.3" />

Azure-szolgáltatásokon (Functions, App Service stb.) futó alkalmazások esetén:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />

Személyes hozzáférési jogkivonat hitelesítése

Fejlesztési és tesztelési forgatókönyvek esetén:

<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="19.225.1" />

C#-kód példák

Az alábbi példák bemutatják, hogyan lehet lekérni a feladatokat különböző hitelesítési módszerekkel.

1. példa: Microsoft Entra ID-hitelesítés (interaktív)

// 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"]}");
        }
    }
}

2. példa: Szolgáltatás főazonosító hitelesítés (automatizált forgatókönyvek)

// 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>();
            }
        }
    }
}

3. példa: Felügyelt identitás hitelesítése (Azure által üzemeltetett alkalmazások)

// 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>();
            }
        }
    }
}

4. példa: Személyes hozzáférési jogkivonat hitelesítése

// 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>();
            }
        }
    }
}

Használati példák

Microsoft Entra ID-hitelesítés használata (interaktív)

class Program
{
    static async Task Main(string[] args)
    {
        var executor = new EntraIdQueryExecutor("your-organization-name");
        await executor.PrintOpenBugsAsync("your-project-name");
    }
}

Szolgáltatásprincípál-hitelesítés használata (CI/CD-forgatókönyvek)

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"]}");
        }
    }
}

Felügyelt identitáshitelesítés (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"]
            })
        });
    }
}

Személyes hozzáférési jogkivonat-hitelesítés használata (fejlesztés/tesztelés)

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"]}");
        }
    }
}

Ajánlott eljárások

Hitelesítés

  • A Microsoft Entra ID használata interaktív alkalmazásokhoz felhasználói bejelentkezéssel
  • Szolgáltatásnév használata automatizált forgatókönyvekhez, CI-/CD-folyamatokhoz és kiszolgálóalkalmazásokhoz
  • Felügyelt identitás használata Azure-szolgáltatásokon futó alkalmazásokhoz (Functions, App Service, virtuális gépek)
  • Kerülje a személyes hozzáférési jogkivonatokat éles környezetben; csak fejlesztéshez és teszteléshez használható
  • Soha ne kódoljon hitelesítő adatokat a forráskódban; környezeti változók vagy Azure Key Vault használata
  • Hitelesítő adatok rotálásának implementálása hosszú ideig futó alkalmazásokhoz
  • Megfelelő hatókörök biztosítása: A munkaelem-lekérdezésekhez megfelelő olvasási engedélyekre van szükség az Azure DevOpsban

Hibakezelés

  • Újrapróbálkozási logika implementálása exponenciális visszalépéssel átmeneti hibák esetén
  • Hibakereséshez és megfigyeléshez naplózza megfelelően a hibákat
  • Bizonyos kivételek, például a hitelesítési hibák és a hálózati időtúllépések kezelése
  • Lemondási tokenek használata hosszú ideig futó műveletekhez

Teljesítmény

  • Több elem lekérdezésekor kötegelt munkaelem-lekérések
  • Lekérdezési eredmények korlátozása nagy adathalmazok TOP záradékával
  • Gyorsítótárazza a gyakran használt adatokat az API-hívások csökkentése érdekében
  • A megfelelő mezők használata az adatátvitel minimalizálásához

Lekérdezésoptimalizálás

  • A jobb teljesítmény érdekében használjon adott mezőneveket a SELECT * helyett
  • Adjon hozzá megfelelő WHERE záradékokat az eredmények szűréséhez a kiszolgálón
  • A használati esetnek megfelelő megrendelési eredmények
  • Fontolja meg a lekérdezési korlátokat és a lapozást nagy eredményhalmazok esetén

Hibaelhárítás

Hitelesítési problémák

  • Microsoft Entra-azonosító hitelesítési hibái: Győződjön meg arról, hogy a felhasználó rendelkezik a megfelelő engedélyekkel, és bejelentkezett az Azure DevOpsba
  • Szolgáltatásnév hitelesítési hibái: Ellenőrizze, hogy az ügyfélazonosító, a titkos kód és a bérlőazonosító helyes-e; szolgáltatásnév-engedélyek ellenőrzése az Azure DevOpsban
  • Felügyelt identitás hitelesítési hibái: Győződjön meg arról, hogy az Azure-erőforrás rendelkezik engedélyezett felügyelt identitással és megfelelő engedélyekkel
  • PAT-hitelesítési hibák: Ellenőrizze, hogy a jogkivonat érvényes-e, és megfelelő hatókörrel rendelkezik-e (vso.work a munkaelem-hozzáféréshez)
  • Jogkivonat lejárata: Ellenőrizze, hogy a PAT lejárt-e, és szükség esetén hozzon létre egy újat

Lekérdezéssel kapcsolatos problémák

  • Érvénytelen WIQL-szintaxis: Győződjön meg arról, hogy a munkaelem lekérdezési nyelvének szintaxisa helyes
  • Projektnévvel kapcsolatos hibák: Ellenőrizze, hogy létezik-e a projekt neve, és helyesen van-e beírva
  • Mezőnévvel kapcsolatos hibák: Használja a megfelelő rendszermezőneveket (például System.Id, System.Title)

Gyakori kivételek

  • VssUnauthorizedException: Hitelesítési adatok és engedélyek ellenőrzése
  • ArgumentException: Ellenőrizze, hogy az összes szükséges paraméter meg van-e adva és érvényes-e
  • HttpRequestException: A hálózati kapcsolat és a szolgáltatás rendelkezésre állásának ellenőrzése

Teljesítménnyel kapcsolatos problémák

  • Lassú lekérdezések: Adjon hozzá megfelelő WHERE záradékokat és korlátozza az eredményhalmazokat
  • Memóriahasználat: Nagy eredményhalmazok feldolgozása kötegekben
  • Sebességkorlátozás: Újrapróbálkozási logika implementálása exponenciális visszalépéssel

Következő lépések