Compartilhar via


Exemplos de biblioteca de clientes SOAP para o Azure DevOps

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

Aviso

Tecnologia herdada - alternativas modernas recomendadas

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

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

Para desenvolvimento novo, use as bibliotecas de clientes .NET modernas baseadas em REST que oferecem:

  • ✅ Melhor desempenho e confiabilidade
  • ✅ 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 recursos modernos do C#
  • ✅ Desenvolvimento ativo e suporte

Este artigo contém exemplos de integração com o Azure DevOps Server e o Azure DevOps Services 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 do Windows para suporte ao cliente SOAP

Limitações de :

  • ❌ Sem suporte para .NET Core ou .NET 5+
  • ❌ Opções de autenticação moderna limitadas
  • ❌ Sem padrões assíncronos/de espera
  • ❌ Desempenho reduzido em comparação com clientes REST
  • ❌ Suporte e atualizações futuros limitados

Pacotes NuGet necessários:

Diretrizes de migração

Etapa 1: Avaliar o uso atual

  • Identificar a funcionalidade específica de SOAP que seu aplicativo usa
  • Determinar se as 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 a ID do Microsoft Entra
  • Curto prazo: migrar para clientes baseados em REST mantendo o .NET Framework
  • Longo prazo: modernizar para .NET Core/.NET 5+ com clientes REST

Etapa 3: Implementar a migração

  • Comece com atualizações de autenticação. Veja os exemplos a seguir.
  • Substituir clientes SOAP por equivalentes REST incrementalmente
  • Teste minuciosamente antes de implantar em produção

Para obter diretrizes de migração detalhadas, consulte exemplos de biblioteca de clientes do .NET.

Exemplos de cliente SOAP legado

Uso básico do cliente SOAP

Importante

Este exemplo mostra padrões herdados somente para referência. Use exemplos baseados em REST para o novo desenvolvimento.

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

Aviso

Esses métodos de autenticação têm limitações de segurança. Migre para a autenticação moderna quando 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 (preterida)

Cuidado

A autenticação de nome de usuário/senha é 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 herdado 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();
    }
}

Migração para aplicativos 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

Principais diferenças

Característica SOAP herdado 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 completo de autenticação moderna
Cobertura de API Somente APIs herdadas Cobertura completa da API REST
Suporte futuro Somente manutenção Desenvolvimento ativo
Padrões de código Bloqueio síncrono Padrões assíncronos ou de espera

Solução de problemas de clientes legados

Problemas comuns e soluções

Falhas de autenticação:

  • Verifique se os PATs têm escopos apropriados
  • Verificar o formato de URL da organização (incluir coleção para ambientes locais)
  • Confira as configurações do firewall e do proxy para os pontos de extremidade SOAP

Problemas de desempenho:

  • Os clientes SOAP são inerentemente mais lentos que REST
  • Considere as operações em lote sempre que possível
  • Migrar para clientes REST para melhor desempenho

Compatibilidade da plataforma:

  • Os clientes SOAP só funcionam no .NET Framework
  • Usar clientes REST para suporte multiplataforma

Obtendo ajuda

Para problemas de cliente SOAP herdado:

  1. Verificar a Comunidade de Desenvolvedores do Azure DevOps
  2. Examinar as diretrizes de migração para alternativas modernas
  3. Considere os serviços de migração profissional para aplicativos grandes

Recursos de migração:

Documentação herdada:

Importante

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