如何通过Azure DevOps REST API批量获取TFVC中manifest文件内容?
批量获取Azure DevOps TFVC中Manifest文件内容的问题与解决
我需要在Azure DevOps项目中获取约10000个manifest文件(如packages.config)的内容,但目前只能逐个获取,效率极低。已经通过路径筛选出目标文件,尝试用TfvcHttpClientBase.GetItemsBatch批量获取,但返回的Content字段始终为空,只有ContentMetadata有值。
现有代码
Program.cs
using Microsoft.TeamFoundation.SourceControl.WebApi; AzureRest azureRest = new AzureRest(); var tfvcItems = azureRest.GetTfvcItems(); List<TfvcItemDescriptor> itemDescriptorsList = new List<TfvcItemDescriptor>(); foreach(var item in tfvcItems) { //示例:.NET的manifest文件 if (item.Path.EndsWith("packages.config")) { var itemDescriptor = new TfvcItemDescriptor() { Path = item.Path, RecursionLevel = VersionControlRecursionType.None, Version = "", VersionOption = TfvcVersionOption.None, VersionType = TfvcVersionType.Latest }; itemDescriptorsList.Add(itemDescriptor); } } TfvcItemDescriptor[] itemDescriptorsArray = itemDescriptorsList.ToArray(); var itemBatch = azureRest.GetTfvcItemsBatch(itemDescriptorsArray); foreach(var itemList in itemBatch) { foreach(var itemListList in itemList) { Console.WriteLine("Content: " + itemListList.Content); //为空/Null Console.WriteLine("ContentMetadata: " + itemListList.ContentMetadata); //不为空 } }
AzureRest.cs
using Microsoft.TeamFoundation.SourceControl.WebApi; using Microsoft.VisualStudio.Services.Common; using Microsoft.VisualStudio.Services.WebApi; public class AzureRest { const string ORG_URL = "https://org/url/url"; const string PROJECT = "Project"; const string PAT = "PersonalAccessToken"; private string GetTokenConfig() { return PAT; } private string GetProjectNameConfig() { return PROJECT; } private VssConnection Authenticate() { string token = GetTokenConfig(); string projectName = GetProjectNameConfig(); var credentials = new VssBasicCredential(string.Empty, token); var connection = new VssConnection(new Uri(ORG_URL), credentials); return connection; } public List<TfvcItem> GetTfvcItems() { var connection = Authenticate(); using (TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>()) { var tfvcItems = tfvcClient.GetItemsAsync(scopePath: "/Path", recursionLevel: VersionControlRecursionType.Full, true).Result; return tfvcItems; } } public List<List<TfvcItem>> GetTfvcItemsBatch(TfvcItemDescriptor[] itemDescriptors) { TfvcItemRequestData requestData = new TfvcItemRequestData() { IncludeContentMetadata = true, IncludeLinks = true, ItemDescriptors = itemDescriptors }; var connection = Authenticate(); using (TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>()) { var tfvcItems = tfvcClient.GetItemsBatchAsync(requestData).Result; return tfvcItems; } } } }
解决方法
1. 启用内容返回配置
GetItemsBatch默认不会返回文件内容,必须显式设置IncludeContent = true。修改GetTfvcItemsBatch方法中的TfvcItemRequestData:
TfvcItemRequestData requestData = new TfvcItemRequestData() { IncludeContent = true, // 新增此行,开启内容返回 IncludeContentMetadata = true, IncludeLinks = true, ItemDescriptors = itemDescriptors };
2. 修正版本参数
TfvcItemDescriptor中Version字段为空可能导致版本解析异常,指定最新版本时可将Version设为"T":
var itemDescriptor = new TfvcItemDescriptor() { Path = item.Path, RecursionLevel = VersionControlRecursionType.None, Version = "T", // 明确指定最新版本 VersionOption = TfvcVersionOption.None, VersionType = TfvcVersionType.Latest };
3. 分批处理大量请求
Azure DevOps API对单次批量请求的条目数量有限制(通常不超过200-500条),直接请求10000个文件会触发限流或失败,需要分批处理:
int batchSize = 200; // 根据API限制调整 for (int i = 0; i < itemDescriptorsList.Count; i += batchSize) { var batch = itemDescriptorsList.Skip(i).Take(batchSize).ToArray(); var itemBatch = azureRest.GetTfvcItemsBatch(batch); // 处理当前批次内容 foreach(var itemList in itemBatch) { foreach(var item in itemList) { if (!string.IsNullOrEmpty(item.Content)) { // 业务处理逻辑 Console.WriteLine($"文件路径: {item.Path}"); Console.WriteLine($"内容: {item.Content}"); } } } }
4. 异步优化
避免使用.Result阻塞线程,改用async/await提升性能和稳定性,修改相关方法:
// AzureRest.cs 中修改方法为异步 public async Task<List<TfvcItem>> GetTfvcItems() { var connection = Authenticate(); using (TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>()) { var tfvcItems = await tfvcClient.GetItemsAsync(scopePath: "/Path", recursionLevel: VersionControlRecursionType.Full, true); return tfvcItems; } } public async Task<List<List<TfvcItem>>> GetTfvcItemsBatch(TfvcItemDescriptor[] itemDescriptors) { TfvcItemRequestData requestData = new TfvcItemRequestData() { IncludeContent = true, IncludeContentMetadata = true, IncludeLinks = true, ItemDescriptors = itemDescriptors }; var connection = Authenticate(); using (TfvcHttpClient tfvcClient = connection.GetClient<TfvcHttpClient>()) { var tfvcItems = await tfvcClient.GetItemsBatchAsync(requestData); return tfvcItems; } }
对应Program.cs改为异步入口:
using Microsoft.TeamFoundation.SourceControl.WebApi; async Task Main() { AzureRest azureRest = new AzureRest(); var tfvcItems = await azureRest.GetTfvcItems(); List<TfvcItemDescriptor> itemDescriptorsList = new List<TfvcItemDescriptor>(); foreach(var item in tfvcItems) { if (item.Path.EndsWith("packages.config")) { var itemDescriptor = new TfvcItemDescriptor() { Path = item.Path, RecursionLevel = VersionControlRecursionType.None, Version = "T", VersionOption = TfvcVersionOption.None, VersionType = TfvcVersionType.Latest }; itemDescriptorsList.Add(itemDescriptor); } } int batchSize = 200; for (int i = 0; i < itemDescriptorsList.Count; i += batchSize) { var batch = itemDescriptorsList.Skip(i).Take(batchSize).ToArray(); var itemBatch = await azureRest.GetTfvcItemsBatch(batch); foreach(var itemList in itemBatch) { foreach(var item in itemList) { Console.WriteLine("Content: " + item.Content); Console.WriteLine("ContentMetadata: " + item.ContentMetadata); } } } } await Main();
内容的提问来源于stack exchange,提问作者agoSwitch
相关产品推荐
相关产品推荐

