在返回带取消的IAsyncEnumerable的函数中迭代IAsyncEnumerable [英] Iterating an IAsyncEnumerable in a function returning an IAsyncEnumerable with cancellation

查看:77
本文介绍了在返回带取消的IAsyncEnumerable的函数中迭代IAsyncEnumerable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正如标题所述,我必须执行以下功能:

As the title says, I have to following function:

public async IAsyncEnumerable<Job> GetByPipeline(int pipelineId,
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    await foreach (var job in context.Jobs.Where(job => job.Pipeline.Id == pipelineId)
        .AsAsyncEnumerable()
        .WithCancellation(cancellationToken)
        .ConfigureAwait(false))
    {
        yield return job;
    }
}

我无法确定取消令牌的去向,也无法在太多地方使用它.

I have trouble wrapping my head around where the cancellation token is going, and a nagging feeling that I am using it in too many places.

当您解构所有花哨的异步内容时,这里实际上发生了什么?还有编写此函数的更好方法吗?

What is actually happening here when you deconstruct all the fancy async stuff? And are there any better ways to write this function?

推荐答案

想象一下,实体框架内部的某个地方是方法 GetJobs ,该方法从对象中检索 Job 对象.数据库:

Imagine that somewhere deep inside the Entity Framework is the method GetJobs that retrieves the Job objects form the database:

private static async IAsyncEnumerable<Job> GetJobs(DbDataReader dataReader,
    [EnumeratorCancellation]CancellationToken cancellationToken = default)
{
    while (await dataReader.ReadAsync(cancellationToken))
    {
        yield return new Job()
        {
            Id = (int)dataReader["Id"],
            Data = (byte[])dataReader["Data"]
        };
    }
}

现在想象一下 Data 属性包含一个巨大的字节数组,其中的数据伴随着 Job .检索每个 Job 的数组可能会花费一些不平常的时间.在这种情况下,打破两次迭代之间的循环是不够的,因为在调用 Cancel 方法与提高 OperationCanceledException 之间会有明显的延迟.代码>.这就是为什么方法 DbDataReader.ReadAsync 需要一个 CancellationToken ,以便可以立即取消查询.

Now imagine that the Data property contains a huge byte array with data accosiated with the Job. Retrieving the array of each Job may take some non-trivial amount of time. In this case breaking the loop between iterations would not be enough, because there would be a noticable delay between invoking the Cancel method and the raising of the OperationCanceledException. This is why the method DbDataReader.ReadAsync needs a CancellationToken, so that the query can be canceled instantly.

现在的挑战是,当诸如 context.Jobs 之类的属性时,如何将客户端代码传递的 CancellationToken 传递给 GetJobs 方法.一路走来.解决方案是 WithCancellation 扩展方法,用于存储令牌并将其更深地传递给接受用

The challenge now is how to pass the CancellationToken passed by the client code to the GetJobs method, when a property like context.Jobs is along the way. The solution is the WithCancellation extension method, that stores the token and passes it deeper, to a method accepting an argument decorated with the EnumeratorCancellation attribute.

因此,对于您而言,您已正确完成了所有操作.建议您在 IAsyncEnumerable 返回方法中包含一个 cancellationToken 参数,这是推荐的做法.这样,链接到您的 GetByPipeline 方法的后续 WithCancellation 不会被浪费.然后,将 WithCancellation 链接到

So in your case you have done everything correctly. You have included a cancellationToken argument in your IAsyncEnumerable returning method, which is the recommended practice. This way subsequent WithCancellation chained to your GetByPipeline method will not be wasted. Then you chained the WithCancellation after the AsAsyncEnumerable inside your method, which is also correct. Otherwise the CancellationToken would not reach its final destination, the GetJobs method.

这篇关于在返回带取消的IAsyncEnumerable的函数中迭代IAsyncEnumerable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆