Condividi tramite


Esempi di libreria client SOAP per Azure DevOps

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022

Avvertimento

Tecnologia obsoleta - alternative moderne consigliate

Questi client basati su SOAP sono tecnologie legacy e devono essere usati solo per:

  • Gestione di applicazioni esistenti che non possono essere modernizzate
  • Applicazioni .NET Framework che richiedono funzionalità specifiche di SOAP

Per il nuovo sviluppo, usare le moderne librerie client .NET basate su REST che offrono:

  • ✅ Prestazioni e affidabilità migliori
  • ✅ Supporto per .NET Core, .NET 5+e .NET Framework
  • ✅ Metodi di autenticazione moderni (identità gestite, principali del servizio)
  • ✅ Pattern async/await e funzionalità moderne di C#
  • ✅ Sviluppo e supporto attivi

Questo articolo contiene esempi per l'integrazione con Azure DevOps Server e Azure DevOps Services usando client SOAP legacy. Questi client sono disponibili solo nella versione di .NET Framework e richiedono metodi di autenticazione locali o legacy.

Prerequisiti e limitazioni

Requirements:

  • .NET Framework 4.6.1 o versioni successive
  • Pacchetti NuGet obsoleti
  • Ambiente Windows per il supporto client SOAP

Limitazioni:

  • ❌ Nessun supporto per .NET Core o .NET 5+
  • ❌ Opzioni di autenticazione moderne limitate
  • ❌ Nessun modello async/await
  • ❌ Prestazioni ridotte rispetto ai client REST
  • ❌ Supporto e aggiornamenti futuri limitati

Pacchetti NuGet necessari:

Indicazioni sulla migrazione

Passaggio 1: Valutare l'utilizzo corrente

  • Identificare le funzionalità specifiche di SOAP usate dall'applicazione
  • Determinare se sono disponibili API REST equivalenti
  • Valutare i requisiti di autenticazione

Passaggio 2: Pianificare la strategia di migrazione

  • Immediato: aggiornare l'autenticazione per l'uso di PAT o ID Microsoft Entra
  • A breve termine: eseguire la migrazione a client basati su REST mantenendo .NET Framework
  • A lungo termine: modernizzare a .NET Core/.NET 5+ con client REST

Passaggio 3: Implementare la migrazione

  • Iniziare con gli aggiornamenti dell'autenticazione. Vedere gli esempi seguenti.
  • Sostituire i client SOAP con equivalenti REST in modo incrementale
  • Testare accuratamente prima della distribuzione nell'ambiente di produzione

Per indicazioni dettagliate sulla migrazione, vedere esempi di librerie client .NET.

Esempi di client SOAP legacy

Utilizzo del client SOAP di base

Importante

In questo esempio vengono illustrati modelli legacy solo per riferimento. Usare esempi basati su REST per il nuovo sviluppo.

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Linq;

/// <summary>
/// Legacy SOAP client example - use REST clients for new development
/// Creates a work item query, runs it, and displays results
/// </summary>
public static class LegacySoapExample
{
    public static void ExecuteWorkItemQuery(string collectionUri, string teamProjectName, VssCredentials credentials)
    {
        try
        {
            // Create TfsTeamProjectCollection instance with credentials
            using (var tpc = new TfsTeamProjectCollection(new Uri(collectionUri), credentials))
            {
                // Authenticate the connection
                tpc.Authenticate();
                
                // Get the WorkItemStore service (SOAP-based)
                var workItemStore = tpc.GetService<WorkItemStore>();
                
                // Get the project context
                var workItemProject = workItemStore.Projects[teamProjectName];
                
                // Find 'My Queries' folder
                var myQueriesFolder = workItemProject.QueryHierarchy
                    .OfType<QueryFolder>()
                    .FirstOrDefault(qh => qh.IsPersonal);
                
                if (myQueriesFolder != null)
                {
                    const string queryName = "Legacy SOAP Sample";
                    
                    // Check if query already exists
                    var existingQuery = myQueriesFolder
                        .OfType<QueryDefinition>()
                        .FirstOrDefault(qi => qi.Name.Equals(queryName, StringComparison.OrdinalIgnoreCase));
                    
                    QueryDefinition queryDefinition;
                    if (existingQuery == null)
                    {
                        // Create new query with proper WIQL
                        queryDefinition = new QueryDefinition(
                            queryName,
                            @"SELECT [System.Id], [System.WorkItemType], [System.Title], 
                                     [System.AssignedTo], [System.State], [System.Tags] 
                              FROM WorkItems 
                              WHERE [System.TeamProject] = @project 
                                AND [System.WorkItemType] = 'Bug' 
                                AND [System.State] = 'New'
                              ORDER BY [System.CreatedDate] DESC");
                        
                        myQueriesFolder.Add(queryDefinition);
                        workItemProject.QueryHierarchy.Save();
                    }
                    else
                    {
                        queryDefinition = existingQuery;
                    }
                    
                    // Execute the query
                    var workItems = workItemStore.Query(queryDefinition.QueryText);
                    
                    Console.WriteLine($"Found {workItems.Count} work items:");
                    foreach (WorkItem workItem in workItems)
                    {
                        var title = workItem.Fields["System.Title"].Value;
                        var state = workItem.Fields["System.State"].Value;
                        Console.WriteLine($"#{workItem.Id}: {title} [{state}]");
                    }
                    
                    if (workItems.Count == 0)
                    {
                        Console.WriteLine("No work items found matching the query criteria.");
                    }
                }
                else
                {
                    Console.WriteLine("'My Queries' folder not found.");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error executing SOAP query: {ex.Message}");
            throw;
        }
    }
}

Metodi di autenticazione legacy

Avvertimento

Questi metodi di autenticazione presentano limitazioni di sicurezza. Eseguire la migrazione all'autenticazione moderna , quando possibile.

/// <summary>
/// Authenticate SOAP client using Personal Access Token
/// Most secure option for legacy SOAP clients
/// </summary>
public static void AuthenticateWithPAT(string collectionUri, string personalAccessToken)
{
    try
    {
        var credentials = new VssBasicCredential(string.Empty, personalAccessToken);
        
        using (var tpc = new TfsTeamProjectCollection(new Uri(collectionUri), credentials))
        {
            tpc.Authenticate();
            Console.WriteLine($"Successfully authenticated to: {tpc.DisplayName}");
            Console.WriteLine($"Instance ID: {tpc.InstanceId}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"PAT authentication failed: {ex.Message}");
        throw;
    }
}

Autenticazione Di Microsoft Entra (supporto limitato)

/// <summary>
/// Microsoft Entra authentication for SOAP services
/// Limited to specific scenarios - prefer REST clients for modern auth
/// </summary>
public static void AuthenticateWithEntraID(string collectionUri)
{
    try
    {
        // Note: Limited authentication options compared to REST clients
        var credentials = new VssAadCredential();
        
        using (var tpc = new TfsTeamProjectCollection(new Uri(collectionUri), credentials))
        {
            tpc.Authenticate();
            Console.WriteLine($"Successfully authenticated with Microsoft Entra ID");
            Console.WriteLine($"Collection: {tpc.DisplayName}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Microsoft Entra authentication failed: {ex.Message}");
        Console.WriteLine("Consider migrating to REST clients for better authentication support.");
        throw;
    }
}

Autenticazione interattiva (solo .NET Framework)

/// <summary>
/// Interactive authentication with Visual Studio sign-in prompt
/// Only works in .NET Framework with UI context
/// </summary>
public static void AuthenticateInteractively(string collectionUri)
{
    try
    {
        var credentials = new VssClientCredentials();
        
        using (var tpc = new TfsTeamProjectCollection(new Uri(collectionUri), credentials))
        {
            tpc.Authenticate();
            Console.WriteLine($"Interactive authentication successful");
            Console.WriteLine($"Authenticated user: {tpc.AuthorizedIdentity.DisplayName}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Interactive authentication failed: {ex.Message}");
        Console.WriteLine("Ensure application has UI context and user interaction is possible.");
        throw;
    }
}

Autenticazione con nome utente/password (deprecato)

Attenzione

L'autenticazione con nome utente/password è deprecata e non sicura. Utilizzare piuttosto i PATs o metodi di autenticazione moderni.

/// <summary>
/// Username/password authentication - DEPRECATED AND INSECURE
/// Only use for legacy on-premises scenarios where no alternatives exist
/// </summary>
[Obsolete("Username/password authentication is deprecated. Use PATs or modern authentication.")]
public static void AuthenticateWithUsernamePassword(string collectionUri, string username, string password)
{
    try
    {
        var credentials = new VssAadCredential(username, password);
        
        using (var tpc = new TfsTeamProjectCollection(new Uri(collectionUri), credentials))
        {
            tpc.Authenticate();
            Console.WriteLine("Username/password authentication successful (DEPRECATED)");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Username/password authentication failed: {ex.Message}");
        Console.WriteLine("This method is deprecated. Please migrate to PATs or modern authentication.");
        throw;
    }
}

Esempio legacy completo

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Configuration;

/// <summary>
/// Complete example showing legacy SOAP client usage
/// For reference only - use REST clients for new development
/// </summary>
class LegacySoapProgram
{
    static void Main(string[] args)
    {
        try
        {
            // Get configuration (prefer environment variables or secure config)
            var collectionUri = ConfigurationManager.AppSettings["CollectionUri"];
            var projectName = ConfigurationManager.AppSettings["ProjectName"];
            var personalAccessToken = ConfigurationManager.AppSettings["PAT"]; // Store securely
            
            if (string.IsNullOrEmpty(collectionUri) || string.IsNullOrEmpty(projectName))
            {
                Console.WriteLine("Please configure CollectionUri and ProjectName in app.config");
                return;
            }
            
            Console.WriteLine("=== Legacy SOAP Client Example ===");
            Console.WriteLine("WARNING: This uses deprecated SOAP clients.");
            Console.WriteLine("Consider migrating to REST clients for better performance and support.");
            Console.WriteLine();
            
            VssCredentials credentials;
            
            if (!string.IsNullOrEmpty(personalAccessToken))
            {
                // Recommended: Use PAT authentication
                credentials = new VssBasicCredential(string.Empty, personalAccessToken);
                Console.WriteLine("Using Personal Access Token authentication");
            }
            else
            {
                // Fallback: Interactive authentication (requires UI)
                credentials = new VssClientCredentials();
                Console.WriteLine("Using interactive authentication");
            }
            
            // Execute the legacy SOAP example
            LegacySoapExample.ExecuteWorkItemQuery(collectionUri, projectName, credentials);
            
            Console.WriteLine();
            Console.WriteLine("Example completed successfully.");
            Console.WriteLine("For new development, see: https://docs.microsoft.com/azure/devops/integrate/concepts/dotnet-client-libraries");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            Console.WriteLine();
            Console.WriteLine("Migration recommendations:");
            Console.WriteLine("1. Update to REST-based client libraries");
            Console.WriteLine("2. Use modern authentication (managed identities, service principals)");
            Console.WriteLine("3. Migrate to .NET Core/.NET 5+ for better performance");
            
            Environment.Exit(1);
        }
        
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }
}

Migrazione ai client moderni

Confronto fianco a fianco

Approccio SOAP precedente:

// ❌ Legacy SOAP pattern
using (var tpc = new TfsTeamProjectCollection(uri, credentials))
{
    var workItemStore = tpc.GetService<WorkItemStore>();
    var workItems = workItemStore.Query("SELECT * FROM WorkItems");
    // Synchronous, blocking operations
}

Approccio REST moderno:

// ✅ Modern REST pattern
using var connection = new VssConnection(uri, credentials);
var witClient = connection.GetClient<WorkItemTrackingHttpClient>();
var workItems = await witClient.QueryByWiqlAsync(new Wiql { Query = "SELECT * FROM WorkItems" });
// Asynchronous, non-blocking operations

Differenze principali

Caratteristica / Funzionalità Legacy SOAP REST moderno
Supporto della piattaforma Esclusivamente .NET Framework .NET Framework, .NET Core, .NET 5+
Prestazioni Più lento, sincrono Più veloce, asincrono
Autenticazione Opzioni limitate Supporto completo dell'autenticazione moderna
Copertura API Solo API storici Copertura completa dell'API REST
Supporto futuro Solo manutenzione Sviluppo attivo
Modelli di codice Blocco sincrono Modelli di async/await

Risoluzione dei problemi dei client legacy

Problemi e soluzioni comuni

Errori di autenticazione:

  • Assicurarsi che i PAT dispongano di ambiti appropriati
  • Verificare il formato dell'URL dell'organizzazione (includere la raccolta per l'ambiente locale)
  • Controllare le impostazioni del firewall e del proxy per gli endpoint SOAP

Problemi di prestazioni:

  • I client SOAP sono intrinsecamente più lenti rispetto a REST
  • Prendere in considerazione le operazioni batch laddove possibile
  • Eseguire la migrazione ai client REST per ottenere prestazioni migliori

Compatibilità della piattaforma:

  • I client SOAP funzionano solo in .NET Framework
  • Usare i client REST per il supporto multipiattaforma

Ottenere assistenza

In caso di problemi con il client SOAP legacy:

  1. Controllare la community degli sviluppatori di Azure DevOps
  2. Esaminare le linee guida sulla migrazione per le alternative moderne
  3. Prendere in considerazione i servizi di migrazione professionale per applicazioni di grandi dimensioni

Risorse di migrazione:

Documentazione legacy:

Importante

Pianificazione della migrazione? Iniziare con gli esempi moderni di libreria client .NET per visualizzare le procedure consigliate e le opzioni di autenticazione correnti.