Partilhar via


Exemplos de biblioteca de cliente SOAP para Azure DevOps

Serviços de DevOps do Azure | Azure DevOps Server | Azure DevOps Server 2022

Advertência

Tecnologia legada - alternativas modernas recomendadas

Esses clientes baseados em SOAP são tecnologia legada e só devem ser usados para:

  • Manutenção de aplicativos existentes que não podem ser modernizados
  • Aplicativos .NET Framework que exigem funcionalidade específica do SOAP

Para novos desenvolvimentos, use as modernas bibliotecas de cliente .NET baseadas em REST que oferecem:

  • ✅ Melhor desempenho e fiabilidade
  • ✅ Suporte para .NET Core, .NET 5+ e .NET Framework
  • ✅ Métodos de autenticação modernos (identidades gerenciadas, entidades de serviço)
  • ✅ Padrões async/await e características modernas do C#
  • ✅ Desenvolvimento ativo e apoio

Este artigo contém exemplos para integração com o Servidor de DevOps do Azure e os Serviços de DevOps do Azure usando clientes SOAP herdados. Esses clientes só estão disponíveis na versão do .NET Framework e exigem métodos de autenticação locais ou herdados.

Pré-requisitos e limitações

Requisitos:

  • .NET Framework 4.6.1 ou posterior
  • Pacotes NuGet herdados
  • Ambiente Windows para suporte ao cliente SOAP

Limitações:

  • ❌ Sem suporte a .NET Core ou .NET 5+
  • ❌ Opções de autenticação modernas limitadas
  • ❌ Sem padrões async/await
  • ❌ Desempenho reduzido em comparação com clientes REST
  • ❌ Suporte e atualizações futuros limitados

Pacotes NuGet necessários:

Orientações em matéria de migração

Etapa 1: Avaliar seu uso atual

  • Identificar a funcionalidade específica do SOAP que seu aplicativo usa
  • Determinar se APIs REST equivalentes estão disponíveis
  • Avaliar os requisitos de autenticação

Etapa 2: Planejar a estratégia de migração

  • Imediato: Atualizar a autenticação para usar PATs ou Microsoft Entra ID
  • Curto prazo: migre para clientes baseados em REST mantendo o .NET Framework
  • Longo prazo: Modernize para .NET Core/.NET 5+ com clientes REST

Etapa 3: Implementar a migração

  • Comece com atualizações de autenticação. Veja os exemplos que se seguem.
  • Substitua clientes SOAP por equivalentes REST incrementalmente
  • Teste cuidadosamente antes de implantar na produção

Para obter orientações detalhadas sobre migração, consulte Exemplos de biblioteca de cliente .NET.

Exemplos de clientes SOAP legados

Uso básico do cliente SOAP

Importante

Este exemplo mostra padrões herdados apenas para referência. Use exemplos baseados em REST para novos desenvolvimentos.

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 autenticação herdados

Advertência

Esses métodos de autenticação têm limitações de segurança. Migre para a autenticação moderna sempre que possível.

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

Autenticação do Microsoft Entra (suporte limitado)

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

Autenticação interativa (somente .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;
    }
}

Autenticação de nome de usuário/senha (Preterido)

Atenção

A autenticação de nome de usuário/senha foi preterida e insegura. Em vez disso, use PATs ou métodos de autenticação 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;
    }
}

Exemplo completo de sistema legado

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

Migração para clientes modernos

Comparação lado a lado

Abordagem SOAP herdada:

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

Abordagem REST moderna:

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

Diferenças principais

Característica Legado SOAP REST moderno
Suporte à plataforma Somente .NET Framework .NET Framework, .NET Core, .NET 5+
Desempenho Mais lento, síncrono Mais rápido, assíncrono
Autenticação Opções limitadas Suporte de autenticação moderno completo
Cobertura da API Somente APIs herdadas Cobertura completa da API REST
Suporte Futuro Apenas manutenção Desenvolvimento ativo
Padrões de código Bloqueio síncrono Padrões de 'async/await'

Resolução de problemas de clientes legados

Problemas e soluções comuns

Falhas de autenticação:

  • Garantir que os PATs tenham escopos apropriados
  • Verificar o formato da URL da organização (incluindo a coleção para a instalação local)
  • Verifique as definições de firewall e proxy para os endpoints SOAP

Problemas de desempenho:

  • Os clientes SOAP são inerentemente mais lentos do que os REST
  • Considerar operações em lote sempre que possível
  • Migre para clientes REST para obter um melhor desempenho

Compatibilidade da plataforma:

  • Clientes SOAP só funcionam no .NET Framework
  • Use clientes REST para suporte entre plataformas

Obter ajuda

Problemas com clientes SOAP antigos:

  1. Verifique a Comunidade de Desenvolvedores do Azure DevOps
  2. Rever as orientações em matéria de migração para encontrar alternativas modernas
  3. Considere serviços de migração profissional para aplicativos grandes

Recursos de migração:

Documentação de legado:

Importante

Planejando a migração? Comece com exemplos modernos de biblioteca de cliente .NET para ver as práticas recomendadas e as opções de autenticação atuais.