Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022
경고
레거시 기술 - 최신 대안 권장
이러한 SOAP 기반 클라이언트는 레거시 기술이며 다음 용도로만 사용해야 합니다.
- 현대화할 수 없는 기존 애플리케이션 유지 관리
- SOAP 관련 기능이 필요한 .NET Framework 애플리케이션
새 개발의 경우 다음을 제공하는 최신 REST 기반 .NET 클라이언트 라이브러리를 사용합니다.
- ✅ 성능 및 안정성 향상
- ✅ .NET Core, .NET 5 이상 및 .NET Framework 지원
- ✅ 최신 인증 방법(관리 ID, 서비스 주체)
- ✅ 비동기/await 패턴 및 최신 C# 기능
- ✅ 활성 개발 및 지원
이 문서에는 레거시 SOAP 클라이언트를 사용하여 Azure DevOps Server 및 Azure DevOps Services와 통합하기 위한 샘플이 포함되어 있습니다. 이러한 클라이언트는 .NET Framework 버전에서만 사용할 수 있으며 온-프레미스 또는 레거시 인증 방법이 필요합니다.
필수 구성 요소 및 제한 사항
요구 사항:
- .NET Framework 4.6.1 이상
- 레거시 NuGet 패키지
- SOAP 클라이언트 지원을 위한 Windows 환경
제한 사항:
- ❌ .NET Core 또는 .NET 5 이상 지원 없음
- ❌ 제한된 최신 인증 옵션
- ❌ 비동기/대기 패턴 없음
- ❌ REST 클라이언트에 비해 성능 저하
- ❌ 제한된 향후 지원 및 업데이트
필수 NuGet 패키지:
- Microsoft.TeamFoundationServer.ExtendedClient - 레거시 SOAP 클라이언트
- Microsoft.TeamFoundationServer.Client - 핵심 Azure DevOps API
- Microsoft.VisualStudio.Services.Client - 연결 및 인증
- Microsoft.VisualStudio.Services.InteractiveClient - 대화형 인증 흐름
마이그레이션 지침
권장되는 마이그레이션 경로
1단계: 현재 사용량 평가
- 애플리케이션에서 사용하는 SOAP 관련 기능 식별
- 동등한 REST API를 사용할 수 있는지 확인
- 인증 요구 사항 평가
2단계: 마이그레이션 전략 계획
- 즉시 실행: Microsoft Entra ID를 사용하도록 인증 업데이트
- 단기: .NET Framework를 유지하면서 REST 기반 클라이언트로 마이그레이션
- 장기: REST 클라이언트를 사용하여 .NET Core/.NET 5 이상으로 현대화
3단계: 마이그레이션 구현
- 인증 업데이트로 시작합니다. 다음 예제를 참조하세요.
- SOAP 클라이언트를 증분 방식으로 REST 등가물로 바꾸기
- 프로덕션에 배포하기 전에 철저히 테스트
자세한 마이그레이션 지침은 .NET 클라이언트 라이브러리 샘플을 참조하세요.
레거시 SOAP 클라이언트 예제
기본 SOAP 클라이언트 사용
중요합니다
이 예제에서는 참조에 대해서만 레거시 패턴을 보여 줍니다. 새 개발에 REST 기반 샘플을 사용합니다.
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;
}
}
}
레거시 인증 방법
경고
이러한 인증 방법에는 보안 제한이 있습니다. 가능하면 최신 인증 으로 마이그레이션합니다.
개인 액세스 토큰 인증(권장되지 않음)
중요합니다
위험 수준이 높은 개인용 액세스 토큰보다 더 안전한 Microsoft Entra토큰을 사용하는 것이 좋습니다. 자세한 내용은 PAT 사용량 줄이기를 참조하세요. 인증 지침을 검토하여 요구 사항에 적합한 인증 메커니즘을 선택합니다.
PAT를 사용해야 하는 경우 개인용 액세스 토큰을 사용하여 만들기를 참조하세요.
그런 다음 VssBasicCredential로 전달합니다.
var credentials = new VssBasicCredential(string.Empty, personalAccessToken);
using (var tpc = new TfsTeamProjectCollection(new Uri(collectionUri), credentials))
{
tpc.Authenticate();
}
Microsoft Entra 인증(제한된 지원)
/// <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;
}
}
대화형 인증(.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;
}
}
사용자 이름/암호 인증(사용되지 않음)
주의
사용자 이름/암호 인증은 더 이상 사용되지 않으며 안전하지 않습니다. 대신 최신 인증 방법을 사용합니다.
/// <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 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. Migrate to modern authentication.");
throw;
}
}
전체 레거시 예제
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))
{
// Use PAT authentication (consider migrating to modern auth)
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();
}
}
최신 클라이언트로 마이그레이션
나란히 비교
레거시 SOAP 접근 방식:
// ❌ 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
}
최신 REST 접근 방식:
// ✅ 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
주요 차이점
| 특징 | 레거시 SOAP | 최신 REST |
|---|---|---|
| 플랫폼 지원 | .NET Framework만 | .NET Framework, .NET Core, .NET 5 이상 |
| 성능 | 느리고 동기적 | 더 빠른 비동기 처리 |
| 인증 | 제한된 옵션 | 전체 최신 인증 지원 |
| API 적용 범위 | 레거시 API만 | REST API 검사 완료 |
| 향후 지원 | 유지 관리만 | 활성 개발 |
| 코드 패턴 | 동기 차단 | 비동기/대기 패턴 |
레거시 클라이언트 문제 해결
일반적인 문제 및 해결 방법
인증 실패:
- PAT에 적절한 범위가 있는지 확인
- 조직 URL 형식 확인(온-프레미스에 대한 컬렉션 포함)
- SOAP 엔드포인트에 대한 방화벽 및 프록시 설정 확인
성능 문제:
- SOAP 클라이언트는 기본적으로 REST보다 느립니다.
- 가능한 경우 일괄 처리 작업 고려
- 성능 향상을 위해 REST 클라이언트로 마이그레이션
플랫폼 호환성:
- SOAP 클라이언트는 .NET Framework에서만 작동합니다.
- 플랫폼 간 지원을 위해 REST 클라이언트 사용
도움 받기
레거시 SOAP 클라이언트 문제의 경우:
- Azure DevOps 개발자 커뮤니티 확인
- 최신 대안에 대한 마이그레이션 지침 검토
- 대규모 애플리케이션에 대한 전문 마이그레이션 서비스 고려
관련 리소스
마이그레이션 리소스:
- 최신 .NET 클라이언트 라이브러리 샘플 - 권장 대체
- 인증 지침 - 최신 인증 옵션
- Azure DevOps REST API 참조 - 전체 API 설명서
레거시 설명서:
중요합니다
마이그레이션을 계획하고 있나요? 최신 .NET 클라이언트 라이브러리 샘플부터 시작하여 현재 모범 사례 및 인증 옵션을 확인합니다.