问题描述
正如标题所述,我必须执行以下功能:
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
扩展方法,用于存储令牌并将其更深地传递给接受用 EnumeratorCancellation
属性.
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
链接到 AsAsyncEnumerable
在您的方法中,这也是正确的.否则, CancellationToken
不会到达其最终目的地,即 GetJobs
方法.
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!