1

TFS で AsyncCTP を使用してみます。現在、TFS クエリ インスタンスで RunQuery を呼び出す長時間実行されるメソッドがあります。

Query は、APM メソッドの BeginQuery() および EndQuery() を公開します。私が理解しているように、AsyncCTP を使用してこれらをラップするための推奨されるアプローチは次のようなものです: (ドキュメントの例)

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);
} 

...しかし、これはコンパイルに失敗します。率直に言って、型と形式が正しいように見えるため、実際には理解できない「無効な引数」のインテリセンス エラーが発生します。考えられる問題の 1 つは、クエリ APM メソッドが ICanceleableAsyncResult を期待しているのに対し、Task ファクトリは IAsyncResult を期待している可能性がありますが、TFS API を見ると、ICanceleableAsyncResult は IAsyncResult の特殊化です。

私がそれを間違っているのか、それとも単に不可能なのかはわかりません。AsyncCTP の方法で実行できるようになりたいと思っていますが、APM パターンに戻る必要があるかもしれません。

4

1 に答える 1

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);
  }
}
于 2011-09-22T16:46:08.897 に答える