你需要create APM wrappers around a TAP method。
这并不完全简单,尤其是因为 TokenProvider 的 End* 方法不遵循 APM 模式。
要求和建议:
-
Task 实现了IAsyncResult,所以你可以返回一个任务。
-
state 成员必须从该任务的 IAsyncResult.AsyncState 属性返回,因此您返回的任务不能是由 async 生成的 - 您必须通过自己创建它TaskCompletionSource<T>。
- 操作完成时需要调用
callback。
- 如果您覆盖
Begin*,您必须也覆盖匹配的End*。
生成的代码最终会有点复杂。您可以通过在我的AsyncEx.Tasks 库中使用ApmAsyncFactory.ToBegin 来避免样板(目前,ApmAsyncFactory 仅在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;
}
或者,如果您想了解香肠是如何制作的并全部手工完成,那么一种选择是(记住所有例外情况都可能发生在哪里):
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;
}