Compartir a través de


Ejemplos de biblioteca cliente SOAP para Azure DevOps

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

Advertencia

Tecnología heredada: alternativas modernas recomendadas

Estos clientes basados en SOAP son tecnología heredada y solo se deben usar para:

  • Mantenimiento de aplicaciones existentes que no se pueden modernizar
  • Aplicaciones de .NET Framework que requieren funcionalidad específica de SOAP

Para el desarrollo nuevo, use las bibliotecas cliente de .NET modernas basadas en REST que ofrecen:

  • ✅ Mejor rendimiento y confiabilidad
  • ✅ Compatibilidad con .NET Core, .NET 5 y .NET Framework
  • ✅ Métodos de autenticación modernos (identidades administradas, entidades de servicio)
  • ✅ Patrones de async/await y funciones modernas de C#
  • ✅ Desarrollo y soporte técnico activos

Este artículo contiene ejemplos para la integración con Azure DevOps Server y Azure DevOps Services mediante clientes SOAP heredados. Estos clientes solo están disponibles en la versión de .NET Framework y requieren métodos de autenticación locales o heredados.

Requisitos previos y limitaciones

Requisitos:

  • .NET Framework 4.6.1 o versiones posteriores
  • Paquetes NuGet antiguos
  • Entorno de Windows para soporte de cliente SOAP

Limitaciones:

  • ❌ No se admite .NET Core ni .NET 5+
  • ❌ Opciones de autenticación moderna limitadas
  • ❌ No hay patrones de async/await
  • ❌ Rendimiento reducido en comparación con los clientes REST
  • ❌ Compatibilidad y actualizaciones futuras limitadas

Paquetes NuGet necesarios:

Guía de migración

Paso 1: Evaluación del uso actual

  • Identificación de la funcionalidad específica de SOAP que usa la aplicación
  • Determinar si las API REST equivalentes están disponibles
  • Evaluación de los requisitos de autenticación

Paso 2: Planear la estrategia de migración

  • Inmediato: actualización de la autenticación para usar PAT o Microsoft Entra ID
  • A corto plazo: Migración a clientes basados en REST al tiempo que mantiene .NET Framework
  • A largo plazo: modernización a .NET Core/.NET 5+ con clientes REST

Paso 3: Implementar la migración

  • Comience con las actualizaciones de autenticación. Consulte los ejemplos siguientes.
  • Reemplazar clientes SOAP por equivalentes REST incrementalmente
  • Prueba exhaustiva antes de realizar la implementación en producción

Para obtener instrucciones detalladas sobre la migración, consulte ejemplos de biblioteca cliente de .NET.

Ejemplos de clientes SOAP heredados

Uso básico del cliente SOAP

Importante

En este ejemplo se muestran los patrones heredados solo para referencia. Utiliza ejemplos que utilicen REST para el nuevo desarrollo.

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étodos de autenticación heredados

Advertencia

Estos métodos de autenticación tienen limitaciones de seguridad. Migre a la autenticación moderna siempre que sea posible.

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

Autenticación de Microsoft Entra (compatibilidad limitada)

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

Autenticación interactiva (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;
    }
}

Autenticación de nombre de usuario y contraseña (en desuso)

Precaución

La autenticación de nombre de usuario y contraseña está en desuso y no es segura. En su lugar, use PAT o métodos de autenticación modernos.

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

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

Migración a clientes de software modernos

Comparación en paralelo

Enfoque SOAP heredado:

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

Enfoque DE 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

Principales diferencias

Característica SOAP heredado REST moderno
Compatibilidad con la plataforma Solo para .NET Framework .NET Framework, .NET Core, .NET 5+
Rendimiento Más lento, sincrónico Más rápido, asincrónico
Autenticación Opciones limitadas Compatibilidad completa con la autenticación moderna
Cobertura de API Solo API heredadas Cobertura completa de la API REST
Soporte técnico futuro Solo mantenimiento Desarrollo activo
Patrones de código Bloqueo sincrónico Patrones asincrónicos y await

Solución de problemas de clientes heredados

Problemas comunes y soluciones

Errores de autenticación:

  • Asegurarse de que los PAT tienen ámbitos adecuados
  • Comprobar el formato de dirección URL de la organización (incluir recopilación para el entorno local)
  • Comprobación de la configuración de firewall y proxy para los puntos de conexión SOAP

Problemas de rendimiento:

  • Los clientes SOAP son intrínsecamente más lentos que REST
  • Considere las operaciones por lotes siempre que sea posible.
  • Migración a clientes REST para mejorar el rendimiento

Compatibilidad de la plataforma:

  • Los clientes SOAP solo funcionan en .NET Framework
  • Uso de clientes REST para la compatibilidad multiplataforma

Obtener ayuda

Para cuestiones de clientes SOAP de versiones anteriores:

  1. Consulta la Comunidad de desarrolladores de Azure DevOps
  2. Revisión de la guía de migración para alternativas modernas
  3. Considere la posibilidad de servicios de migración profesionales para aplicaciones grandes

Recursos de migración:

Documentación heredada:

Importante

¿Planeación de la migración? Comience con ejemplos modernos de biblioteca cliente de .NET para ver los procedimientos recomendados actuales y las opciones de autenticación.