如何在实际迭代发生之前验证 IAsyncEnumerable 返回方法的参数?

Posted

技术标签:

【中文标题】如何在实际迭代发生之前验证 IAsyncEnumerable 返回方法的参数?【英文标题】:How to validate arguments for IAsyncEnumerable returning method before actual iteration takes place? 【发布时间】:2020-12-09 02:20:11 【问题描述】:

我有一个简单的场景,我有一个具有以下方法的类:

public async IAsyncEnumerable<Entity> GetEntities(IQueryOptions options)
  if(!validator.ValidateQuery(options))  throw new ArgumentException(nameof(options));

  var data = dataSource.ReadEntitiesAsync(options);

  await foreach (var entity in data)  yield return await converter.ConvertAsync(entity);

是否可以在 GetEntities() 方法调用时准确地抛出 ArgumentException,而不是像这里这样在迭代的第一步之后:

await foreach(var e in GetEntities(options))  // some code here 

我问是因为当我想将 IAsyncEnumerable 返回到我的 API 控制器时,框架代码中实际上会引发异常。我没有机会抓住它,并返回一个 HTTP 404 BAD REQUEST 代码。当然我可以拦截请求管道中的异常,但有时我想根据它们来自的抽象层将它们包装在其他异常中。

【问题讨论】:

为什么在致电GetEntities 之前不进行验证?枚举器不是用来抛出异常的 您可以编写一个非async的包装方法,并在返回async方法的结果之前同步执行验证。不过,这是否有意义地改变了异常发生的时刻取决于await foreach 是如何开展业务的(我不知道)。 @JeroenMostert 是的,这实际上是我目前使用的解决方法。无论如何,有时它会导致某些接口方法无法有效地屏蔽无效参数或空值。 相关:Method having yield return is not throwing exception。 IEnumerableIAsyncEnumerable 迭代器在这方面是相似的。 【参考方案1】:

把它分成两个函数。 Here 是一个例子:

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
                    
public class Program

    public static async Task Main()
    
        var enumeration = AsyncEnumeration(-1);
        await foreach(int x in enumeration)
        
            Console.WriteLine(x);
        
    
    
    public static IAsyncEnumerable<int> AsyncEnumeration(int count)
    
        if (count < 1)
            throw new ArgumentOutOfRangeException();
        
        return AsyncEnumerationCore(count);
    
    
    private static async IAsyncEnumerable<int> AsyncEnumerationCore(int count)
    
        for (int i = 0; i < count; i++)
        
            yield return i;
            await Task.Delay(1);
        
    

【讨论】:

旁白:您可以将private 方法作为嵌套函数放在public 方法中。在这种情况下,甚至可以选择将嵌套方法设为非static 并完全省略count 参数。 (是否做很大程度上取决于口味,暂时假设这不是某种必须测试替代品的性能热点。它确实使得在没有验证的情况下意外调用该方法是不可能的不过在课堂上。) 是的,这只是普通功能,您可以为所欲为。在我的示例中它只是静态的。 也许这是另一个问题的情况,但是:这个解决方案对于 IAsyncEnumerable 是否可行,其中检查也在异步方法中进行? 但是我没有测试。我认为如果你不使用 yield,它就不会成为状态机。

以上是关于如何在实际迭代发生之前验证 IAsyncEnumerable 返回方法的参数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥模型字段验证发生在 validate 或 validate_<field> 被调用之前?

登录验证需要在实际登录之前进行

没有发生之前的安全发布?无论如何除了决赛?

完成 GeneratorDataset 迭代器时发生错误

在实际调用我的 passwordMatch 函数之前,Passport 使用“错误密码”进行身份验证失败

如果发生错误,如何在Matlab中重复循环迭代