基于查询参数并行调用API任务的异常问题及实现方案问询
问题分析与解决方案
原代码抛出ArgumentException的原因
Task.WhenAll的参数不允许包含null任务,你原代码中当users或version参数未被请求时,会给getUsers或VersionTask赋值为null,传入Task.WhenAll后就触发了这个异常。
优化后代码的潜在问题
你后来用列表存储任务的方式避免了null,但依赖列表索引(appTasks[1]、appTasks[2])获取结果的逻辑有漏洞:
- 若只请求了用户数据,列表里只有2个任务,取
appTasks[2]会触发索引越界异常 - 若只请求了版本数据,列表里也是2个任务,取
appTasks[1]会拿到版本任务的结果,导致类型转换错误
正确实现方式
单独保存每个可选任务的引用,只将需要执行的任务加入集合,既避免null,又能安全获取结果:
// 1. 启动主任务 Task<AppData> appDataTask = myClient.GetAppDataAsync(identifierId, identifierName, version, token); // 2. 初始化可选任务,仅当需要时创建实例 Task<Users> usersTask = null; if (users.HasValue && users.Value) { usersTask = FetchUsers(titleId: appId, token: token); } Task<VersionedApp> versionTask = null; if (version.HasValue && version.Value) { versionTask = FetchVersionApp(titleId: TitleId, token: token); } // 3. 收集所有非null的任务,避免传入null给Task.WhenAll List<Task> tasksToAwait = new List<Task> { appDataTask }; if (usersTask != null) tasksToAwait.Add(usersTask); if (versionTask != null) tasksToAwait.Add(versionTask); // 4. 等待所有任务完成 await Task.WhenAll(tasksToAwait); // 5. 安全获取结果(await后用Result不会阻塞,也可以直接await任务) AppData app = await appDataTask; AppResponse response = app.ConvertData(); if (usersTask != null) { DeployedUsers users = await usersTask; response.Users = users; } if (versionTask != null) { VersionedApp versionedApp = await versionTask; response.version = versionedApp; }
针对你优化后的OData场景的适配写法
如果是基于OData的扩展请求判断,同样用单独保存任务的方式,避免索引依赖:
bool? usersRequested = mosAppMetadataRequested?.Any(x => x.PathToNavigationProperty.FirstSegment.Identifier.Equals( AAEConstants.ODataConstants.ODataExpandDeployedTo, StringComparison.CurrentCultureIgnoreCase)); bool? isVersionRequested = mosAppMetadataRequested?.Any(x => x.PathToNavigationProperty.FirstSegment.Identifier.Equals( AAEConstants.ODataConstants.ODataExpandAvailableTo, StringComparison.CurrentCultureIgnoreCase)); // 启动主任务 Task<AppData> appDataTask = mosAdminApiClient.GetTitlePreviewAsync(identifierId, identifierName, version, token); // 初始化可选任务 Task<Users> deployedToTask = null; if (usersRequested.HasValue && usersRequested.Value) { deployedToTask = GetDeployDataByTitleIdAsync(titleId: unifiedAppRequest.TitleId, token: token); } Task<VersionInfo> availableToTask = null; if (isVersionRequested.HasValue && isVersionRequested.Value) { availableToTask = GetAvailableToDataByTitleIdAsync(titleId: unifiedAppRequest.TitleId, token: token); } // 收集任务并等待 List<Task> tasks = new List<Task> { appDataTask }; if (deployedToTask != null) tasks.Add(deployedToTask); if (availableToTask != null) tasks.Add(availableToTask); await Task.WhenAll(tasks); // 处理结果 UnifiedAppResponse response = (await appDataTask).ConvertPreviewToUnifiedAppResponse(); if (deployedToTask != null) { response.DeployedTo = await deployedToTask; } if (availableToTask != null) { response.AvailableTo = await availableToTask; }
关键改进点
- 绝不向
Task.WhenAll传入null任务,只收集需要执行的非null任务 - 单独保存每个可选任务的引用,不用列表索引定位,避免越界和类型错误
- 用
await获取任务结果,比直接访问Result更规范(即使任务已完成,await也不会有性能问题,且语义更清晰)
内容的提问来源于stack exchange,提问作者Programmerzzz
相关产品推荐
相关产品推荐

