如何将AsyncCTP与TFS APM方法(Query.Begin / EndQuery)一起使用?

时间:2011-09-22 16:21:17

标签: async-ctp

想尝试将AsyncCTP与TFS一起使用。目前有一个长时间运行的方法,它在TFS查询实例上调用RunQuery。

Query公开了APM方法BeginQuery()和EndQuery()。据我了解,使用AsyncCTP包装这些内容的推荐方法如下:(来自docs的例子)

Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, offset, count, null);

此外,已将其包装在文档中的扩展方法中,因此我的实际方法如下所示:

public static Task<WorkItemCollection> RunQueryAsync(this Query query)
{
    if (query== null) 
        throw new ArgumentNullException("Query");

    return Task<WorkItemCollection>.Factory.FromAsync(query.BeginQuery, query.EndQuery, null);
} 

...但是无法编译。得到一个“无效参数”intellisense错误,坦率地说,我无法理解,因为类型和格式看起来是正确的。一个可能的问题可能是Query APM方法期望ICanceleableAsyncResult,而Task工厂期望IAsyncResult - 但是查看TFS API,ICanceleableAsyncResult是IAsyncResult的特化。

不确定我做错了还是不可能。希望能够以AsyncCTP的方式做到这一点,但可能不得不回到APM模式 - 呃!

1 个答案:

答案 0 :(得分:4)

更新:我的Nito.AsyncEx库现在包含TeamFoundationClientAsyncFactory类型,可以使用它来代替在下面滚动您自己的实现。


TFS API并未严格遵循APM模式,因为它不会使用state参数,这会阻止内置TaskFactory.FromAsync工作。

您必须编写自己的FromAsync等效文件,可以使用TaskCompletionSource完成:

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;

public static class TfsUtils<TResult>
{
  public static Task<TResult> FromTfsApm(Func<AsyncCallback, ICancelableAsyncResult> beginMethod, Func<ICancelableAsyncResult, TResult> endMethod, CancellationToken token)
  {
    // Represent the asynchronous operation by a manually-controlled task.
    TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
    try
    {
      // Begin the TFS asynchronous operation.
      var asyncResult = beginMethod(Callback(endMethod, tcs));

      // If our CancellationToken is signalled, cancel the TFS operation.
      token.Register(asyncResult.Cancel, false);
    }
    catch (Exception ex)
    {
      // If there is any error starting the TFS operation, pass it to the task.
      tcs.TrySetException(ex);
    }

    // Return the manually-controlled task.
    return tcs.Task;
  }

  private static AsyncCallback Callback(Func<ICancelableAsyncResult, TResult> endMethod, TaskCompletionSource<TResult> tcs)
  {
    // This delegate will be invoked when the TFS operation completes.
    return asyncResult =>
    {
      var cancelableAsyncResult = (ICancelableAsyncResult)asyncResult;

      // First check if we were canceled, and cancel our task if we were.
      if (cancelableAsyncResult.IsCanceled)
        tcs.TrySetCanceled();
      else
      {
        try
        {
          // Call the TFS End* method to get the result, and place it in the task.
          tcs.TrySetResult(endMethod(cancelableAsyncResult));
        }
        catch (Exception ex)
        {
          // Place the TFS operation error in the task.
          tcs.TrySetException(ex);
        }
      }
    };
  }
}

然后您可以在扩展方法中使用它:

using System.Threading;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

public static class TfsExtensions
{
  public static Task<WorkItemCollection> QueryAsync(this Query query, CancellationToken token = new CancellationToken())
  {
    return TfsUtils<WorkItemCollection>.FromTfsApm(query.BeginQuery, query.EndQuery, token);
  }
}