Как я могу использовать AsyncCTP с методом TFS APM (Query.Begin / EndQuery)? - PullRequest
1 голос
/ 22 сентября 2011

Хотел бы попробовать использовать AsyncCTP с TFS. В настоящее время есть долгосрочный метод, который вызывает RunQuery для экземпляра TFS 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);
} 

... но это не скомпилировать. Получение ошибки intellisense "неверный аргумент", которую, честно говоря, я не могу понять, потому что типы и формат выглядят правильно. Одной из возможных проблем может быть то, что методы Query APM ожидают ICanceleableAsyncResult, тогда как фабрика задач ожидает IAsyncResult - но, глядя на API TFS, ICanceleableAsyncResult является специализацией IAsyncResult.

Не уверен, делаю ли я это неправильно или это просто невозможно. Хотелось бы сделать это AsyncCTP, но, возможно, придется вернуться к шаблону APM - тьфу!

1 Ответ

4 голосов
/ 22 сентября 2011

Обновление: Моя 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);
  }
}
...