在IAsyncResult中使用Async方法 [英] Using Async methods inside IAsyncResult
问题描述
我正在从实现 TokenProvider 类> Microsoft.ServiceBus
命名空间.我要重写以下方法
I am implementing TokenProvider class from Microsoft.ServiceBus
namespace.
I am overriding the following method
protected override IAsyncResult OnBeginGetToken(string appliesTo, string action, TimeSpan timeout, AsyncCallback callback,
object state)
{
var token = GetCustomTokenAsync(appliesTo); //How to await?
return new CompletedAsyncResult<SharedAccessSignatureToken>(token, callback, state);
}
但是正如上面的评论中所述.GetCustomTokenAsync是一个异步方法,如何在不干扰TokenProvider类签名的情况下等待?我想利用GetCustomTokenAsync的异步特性,因此我不愿意使用 .Result
.有解决这个问题的更好方法吗?
But as mentioned in the comment above. GetCustomTokenAsync is an async method and how do I await without disturbing the signature of TokenProvider class? I wanted to make use of async nature of GetCustomTokenAsync and hence I am reluctant to use .Result
. Is there any better way of solving this?
推荐答案
您需要这并非完全简单,尤其是因为 TokenProvider
的 End *
方法不遵循APM模式.
This is not entirely straightforward, especially because the End*
methods of TokenProvider
do not follow the APM pattern.
要求和建议:
-
Task
实现了IAsyncResult
,因此您可以返回任务. - 该任务的
state
成员必须从该属性的IAsyncResult.AsyncState
属性返回,因此您返回的 任务将无法生成通过async
-您必须通过TaskCompletionSource< T>
自己创建它. - 操作完成后,需要调用
回调
. - 如果覆盖
Begin *
,则必须还覆盖匹配的End *
.
Task
implementsIAsyncResult
, so you can return a task.- The
state
member has to be returned from theIAsyncResult.AsyncState
property on that task, so the task you return cannot be one generated byasync
- you have to create it yourself viaTaskCompletionSource<T>
. - The
callback
needs to be invoked when the operation completes. - If you override
Begin*
, you must also override the matchingEnd*
.
生成的代码最终变得有点复杂.您可以在我的 AsyncEx.Tasks ApmAsyncFactory.ToBegin 来避免样板a>库(当前, ApmAsyncFactory
仅位于预发行版本):
The resulting code ends up being a bit complex. You can avoid the boilerplate by using the ApmAsyncFactory.ToBegin
in my AsyncEx.Tasks library (currently, ApmAsyncFactory
is only in the prerelease build):
protected override IAsyncResult OnBeginGetToken(string appliesTo, string action,
TimeSpan timeout, AsyncCallback callback, object state)
{
return ApmAsyncFactory.ToBegin(GetCustomTokenAsync(appliesTo), callback, state);
}
protected override SecurityToken OnEndGetToken(IAsyncResult result,
out DateTime cacheUntil)
{
var ret = ApmAsyncFactory.ToEnd<SharedAccessSignatureToken>(result);
cacheUntil = ...;
return ret;
}
或者,如果您想查看香肠的制作方法以及手工操作,则可以选择一种方法(请记住所有异常情况都可以到达):
Alternatively, if you wanted to see how the sausage is made and do it all by hand, one option would be (keeping in mind where all the exceptions can go):
protected override IAsyncResult OnBeginGetToken(string appliesTo, string action,
TimeSpan timeout, AsyncCallback callback, object state)
{
var tcs = new TaskCompletionSource<SharedAccessSignatureToken>(state,
TaskCreationOptions.RunContinuationsAsynchronously);
var _ = CompleteAsync(GetCustomTokenAsync(appliesTo), callback, tcs);
// _ is ignored; it can never fault.
return tcs.Task;
}
private static async Task CompleteAsync<TResult>(Task<TResult> task,
AsyncCallback callback, TaskCompletionSource<TResult> tcs)
{
try
{
tcs.TrySetResult(await task.ConfigureAwait(false));
}
catch (OperationCanceledException ex)
{
tcs.TrySetCanceled(ex.CancellationToken);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
finally
{
// Invoke callback unsafely on the thread pool, so exceptions are global
if (callback != null)
ThreadPool.QueueUserWorkItem(state => callback((IAsyncResult)state), tcs.Task);
}
}
protected override SecurityToken OnEndGetToken(IAsyncResult result,
out DateTime cacheUntil)
{
var task = (Task<SharedAccessSignatureToken>)result;
var ret = task.GetAwaiter().GetResult();
cacheUntil = ...;
return ret;
}
这篇关于在IAsyncResult中使用Async方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!