在受歧视的联合匹配/切换循环中尽早返回

问题描述 投票:0回答:1

我正在编写一个 API,它具有既可以更新单个项目也可以更新多个项目的端点。在更新多个项目的端点中,我想通过简单地循环所有要更新的项目来重用该函数来更新单个项目。单个更新函数当前具有三个可能的返回值(通过使用

OneOf
通过可区分联合实现):

  1. 更新项目
  2. 一个
    OneOf.NotFound
    -实例,以防更新之前不存在
  3. 一个
    OneOf.Error
    -实例,以防出现其他问题(例如验证错误)

在批量更新端点中,我想尽早返回,以防项目更新导致错误。我尝试使用

Match
提供的
Switch
OneOf
方法,但我不知道如何实现我所描述的行为。这是我当前版本的端点代码:

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));
});

显然,我的

error
中的
Match
情况无法编译,因为它的返回类型无法转换为预期类型,但它应该演示所需的行为。我怎样才能正确地做到这一点,或者我的方法从根本上有缺陷?

P.S.:在写这篇文章时,我想知道是否应该提前返回,以防出现错误。这与找不到项目的情况有点不一致。我可能应该更改代码以不提前返回并聚合错误,或者在出现

NotFound
更新结果的情况下也提前返回。对于此 API,我也可能选择返回聚合值。尽管如此,我还是很想知道如何解决这个问题,因为在其他类似的情况下,可能需要提前返回(例如循环中的“昂贵”调用)。

c# functional-programming discriminated-union
1个回答
0
投票

答案是遍历。总是穿越

如果您对函数式编程感兴趣,请查找遍历。他们通常会解决此类问题。 (FWIW,我有一篇关于遍历的文章已排队,但我还需要几周时间才能发布它。)

也就是说,OP 中显示的代码中发生了很多事情,以至于它几乎不起作用

  • async
    await
    的存在强烈建议IO
  • 实现发生了变化
    state
  • 该实现使用
    foreach
    而不是递归或像
    Select
    Aggregate
    这样的高阶函数。 (但是,正如所指出的,您很可能需要某种遍历。对于基于
    Task
    的集合,这可能是 Task.WhenAllTask.WhenAny,但是 IIRC,这些都不是短路的.)

除非你想完全重写整个实现以将纯函数从不纯的操作中分离出来,否则我建议你这样做:

var storedItems = new List<ShoppingListItem>();
var failedItems = new List<ShoppingListItem>();
var state = (storedItems, failedItems, hasError: false);
foreach (var item in itemsToUpdate)
{
    var updateResult = await UpdateItem(item, dbContext);
    state = updateResult.Match<(List<ShoppingListItem>, List<ShoppingListItem>, bool)>(
        storedItem => { storedItems.Add(storedItem); return state;  },
        notFound => { failedItems.Add(notFound); return state; },
        error => { state.hasError = true; return state; }
        );
    if (state.hasError)
        return Results.BadRequest();
}

await dbContext.SaveChangesAsync();

return Results.Ok(new BulkUpdateResult(storedItems, failedItems));

根本不起作用,但OP中的代码也不起作用,这是一个相当简单的更改,您可以进行。

如果您也想收集错误,请为它们创建一个集合而不是布尔标志,然后将错误添加到

foreach
循环中。

© www.soinside.com 2019 - 2024. All rights reserved.