你需要围绕 TAP 方法创建 APM 包装器.
这并不完全简单,特别是因为End*
的方法TokenProvider
do not遵循 APM 模式。
要求和建议:
-
Task
实施IAsyncResult
,这样您就可以返回任务。
- The
state
会员必须从IAsyncResult.AsyncState
该任务的属性,因此您返回的任务cannot是由以下生成的之一async
- 你必须通过以下方式自己创建它TaskCompletionSource<T>
.
- The
callback
操作完成后需要调用。
- 如果你覆盖
Begin*
, you must也覆盖匹配End*
.
生成的代码最终有点复杂。您可以通过使用来避免样板ApmAsyncFactory.ToBegin
in my AsyncEx.任务图书馆(目前,ApmAsyncFactory
只在预发布版本):
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;
}