Partager via


Exemples de bibliothèques clientes SOAP pour Azure DevOps

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

Avertissement

Technologie héritée - alternatives modernes recommandées

Ces clients BASÉS sur SOAP sont des technologies héritées et doivent être utilisés uniquement pour :

  • Gestion des applications existantes qui ne peuvent pas être modernisées
  • Applications .NET Framework qui nécessitent des fonctionnalités spécifiques à SOAP

Pour le nouveau développement, utilisez les bibliothèques de client .NET modernes basées sur REST qui offrent :

  • ✅ Meilleures performances et fiabilité
  • ✅ Prise en charge de .NET Core, .NET 5+ et .NET Framework
  • ✅ Méthodes d’authentification modernes (identités managées, principaux de service)
  • ✅ Modèles Async/await et fonctionnalités C# modernes
  • ✅ Développement et support actifs

Cet article contient des exemples d’intégration avec Azure DevOps Server et Azure DevOps Services à l’aide de clients SOAP hérités. Ces clients sont disponibles uniquement dans la version du .NET Framework et nécessitent des méthodes d’authentification locales ou héritées.

Conditions préalables et limitations

Exigences:

  • .NET Framework 4.6.1 ou version ultérieure
  • Packages NuGet obsolètes
  • Environnement Windows pour la prise en charge du client SOAP

Limitations :

  • ❌ Aucune prise en charge de .NET Core ou .NET 5+
  • ❌ Options d’authentification modernes limitées
  • ❌ Aucun modèle asynchrone/await
  • ❌ Réduction des performances par rapport aux clients REST
  • ❌ Prise en charge et mises à jour ultérieures limitées

Packages NuGet requis :

Conseils sur la migration

Étape 1 : Évaluer votre utilisation actuelle

  • Identifier les fonctionnalités spécifiques à SOAP que votre application utilise
  • Déterminer si des API REST équivalentes sont disponibles
  • Évaluer les exigences d’authentification

Étape 2 : Planifier la stratégie de migration

  • Immédiat : Mettre à jour l’authentification pour utiliser des PAT ou Microsoft Entra ID
  • Court terme : Migrer vers des clients BASÉS sur REST tout en conservant .NET Framework
  • Long terme : Moderniser vers .NET Core/.NET 5+ avec les clients REST

Étape 3 : Implémenter la migration

  • Commencez par les mises à jour d’authentification. Consultez les exemples suivants.
  • Remplacer les clients SOAP par des équivalents REST de manière incrémentielle
  • Tester soigneusement avant le déploiement en production

Pour obtenir des instructions détaillées sur la migration, consultez les exemples de bibliothèque de client .NET.

Exemples de clients SOAP anciens

Utilisation de base du client SOAP

Importante

Cet exemple montre uniquement les modèles hérités pour référence. Utilisez des exemples BASÉS sur REST pour le nouveau développement.

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;
        }
    }
}

Méthodes d’authentification héritées

Avertissement

Ces méthodes d’authentification présentent des limitations de sécurité. Migrez vers l’authentification moderne lorsque cela est possible.

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

Authentification Microsoft Entra (support limité)

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

Authentification interactive (.NET Framework uniquement)

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

Authentification par nom d’utilisateur/mot de passe (déconseillée)

Avertissement

L’authentification par nom d’utilisateur/mot de passe est déconseillée et non sécurisée. Utilisez plutôt des paTs ou des méthodes d’authentification modernes.

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

Exemple complet hérité

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

Migration vers des clients modernes

Comparaison côte à côte

Approche SOAP héritée :

// ❌ 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
}

Approche REST moderne :

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

Principales différences

Caractéristique SOAP hérité REST moderne
Support de plateforme .NET Framework uniquement .NET Framework, .NET Core, .NET 5+
Niveau de performance Plus lent, synchrone Plus rapide, asynchrone
Authentification Options limitées Assistance complète pour l'authentification moderne
Couverture des API API héritées uniquement Couverture complète de l’API REST
Assistance future Maintenance uniquement Développement actif
Modèles de code Blocage synchrone Modèles Async/await

Résolution des problèmes liés aux anciens clients

Problèmes courants et solutions

Échecs d’authentification :

  • Vérifier que les PAT ont des étendues appropriées
  • Vérifier le format de l’URL de l’organisation (inclure la collection pour les éléments locaux)
  • Vérifier les paramètres de pare-feu et de proxy pour les points de terminaison SOAP

Problèmes de performances :

  • Les clients SOAP sont intrinsèquement plus lents que REST
  • Prendre en compte les opérations de traitement par lots lorsque cela est possible
  • Migrer vers des clients REST pour améliorer les performances

Compatibilité de la plateforme :

  • Les clients SOAP fonctionnent uniquement sur .NET Framework
  • Utiliser des clients REST pour la prise en charge multiplateforme

Obtenir de l’aide

Pour les problèmes liés aux anciens clients SOAP :

  1. Vérifier la communauté des développeurs Azure DevOps
  2. Passer en revue les conseils de migration pour les alternatives modernes
  3. Prendre en compte les services de migration professionnels pour les applications volumineuses

Ressources de migration :

Documentation héritée :

Importante

Planification de la migration ? Commencez par des exemples de bibliothèques clientes .NET modernes pour voir les meilleures pratiques et les options d’authentification actuelles.