如何在判别联合的Match/Switch循环中实现提前返回?
批量更新接口复用单条更新函数的实现问题
问题描述
我正在编写一个包含单条和多条数据更新接口的API,希望在批量更新接口中复用单条更新函数,通过循环处理所有待更新项。单条更新函数借助OneOf实现判别联合,有三种返回值:
- 更新后的条目
- 条目不存在时返回OneOf.NotFound实例
- 出现其他问题(如验证错误)时返回OneOf.Error实例
我希望在批量更新接口中,一旦某条数据更新出错就提前返回。尝试使用OneOf提供的Match和Switch方法,但未能实现预期行为。以下是当前的接口代码:
routes.MapPut(RouteBases.ShoppingList, async (IEnumerable<ShoppingListItem> itemsToUpdate, MariaDbContext dbContext) => { var storedItems = new List<ShoppingListItem>(); var failedItems = new List<ShoppingListItem>(); var state = (storedItems, failedItems); foreach (var item in itemsToUpdate) { var updateResult = await UpdateItem(item, dbContext); state = updateResult.Match<(IEnumerable<ShoppingListItem>, IEnumerable<ShoppingListItem>)>( storedItem => (storedItems.Append(storedItem), failedItems), notFound => (storedItems, failedItems.Append(item)), error => { return Results.BadRequest(); } ); } await dbContext.SaveChangesAsync(); return Results.Ok(new BulkUpdateResult(storedItems, failedItems)); });
显然,Match中的error分支无法编译,因为返回类型不匹配,但该分支体现了我想要的行为。请问如何正确实现,或者我的思路是否存在根本性问题?
附言:撰写此问题时我意识到,仅在出错时提前返回可能与条目不存在的情况不一致。我或许应该改为不提前返回并汇总所有错误,或者在遇到NotFound时也提前返回。对于这个API,我可能会选择汇总结果,但仍想了解如何实现提前返回的方案,因为在其他类似场景(如循环中存在高开销调用)可能需要该功能。
解决方案
方案一:遇到错误提前返回
OneOf的Match方法要求所有分支返回相同类型,因此无法在Match分支中直接返回ActionResult。我们可以通过判断OneOf的具体类型,在循环内直接跳出并返回响应,同时通过事务保证数据一致性(避免部分更新提交):
routes.MapPut(RouteBases.ShoppingList, async (IEnumerable<ShoppingListItem> itemsToUpdate, MariaDbContext dbContext) => { var storedItems = new List<ShoppingListItem>(); var failedItems = new List<ShoppingListItem>(); // 开启事务,确保出错时回滚所有已执行的更新 using var transaction = await dbContext.Database.BeginTransactionAsync(); try { foreach (var item in itemsToUpdate) { var updateResult = await UpdateItem(item, dbContext); if (updateResult.IsT0) // 匹配"更新后的条目"分支 { storedItems.Add(updateResult.AsT0); } else if (updateResult.IsT1) // 匹配"条目不存在"分支 { failedItems.Add(item); } else if (updateResult.IsT2) // 匹配"其他错误"分支 { // 回滚事务,提前返回错误响应 await transaction.RollbackAsync(); return Results.BadRequest(updateResult.AsT2.Message); // 假设Error类型包含Message属性 } } // 所有条目处理完成,提交事务 await dbContext.SaveChangesAsync(); await transaction.CommitAsync(); return Results.Ok(new BulkUpdateResult(storedItems, failedItems)); } catch (Exception ex) { // 捕获意外异常,回滚事务 await transaction.RollbackAsync(); return Results.StatusCode(StatusCodes.Status500InternalServerError); } });
方案二:汇总所有结果(适合当前API场景)
如果选择汇总所有成功、NotFound和错误结果再统一返回,可以使用OneOf的Switch方法分别处理每个分支,收集状态后再生成响应:
routes.MapPut(RouteBases.ShoppingList, async (IEnumerable<ShoppingListItem> itemsToUpdate, MariaDbContext dbContext) => { var storedItems = new List<ShoppingListItem>(); var notFoundItems = new List<ShoppingListItem>(); var errorItems = new List<(ShoppingListItem Item, string ErrorMessage)>(); foreach (var item in itemsToUpdate) { var updateResult = await UpdateItem(item, dbContext); // 用Switch分发不同分支的处理逻辑 updateResult.Switch( storedItem => storedItems.Add(storedItem), _ => notFoundItems.Add(item), error => errorItems.Add((item, error.Message)) ); } // 根据业务需求决定是否提交更新: // 示例1:存在错误则不提交任何更新,返回错误详情 if (errorItems.Any()) { return Results.BadRequest(new { SuccessCount = storedItems.Count, NotFoundCount = notFoundItems.Count, Errors = errorItems }); } // 示例2:无错误则提交所有成功的更新,返回汇总结果 await dbContext.SaveChangesAsync(); return Results.Ok(new BulkUpdateResult(storedItems, notFoundItems)); });
内容的提问来源于stack exchange,提问作者causa prima
相关产品推荐
相关产品推荐

