Как я могу использовать AsyncCTP с методом APM TFS (Query.Begin/EndQuery)?

Хотел бы попробовать использовать 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 — тьфу!


person Tim    schedule 22.09.2011    source источник
comment
Смотрите мой обновленный ответ; На этот раз я скомпилировал его сам, что всегда помогает ;). P.S. Роберт Харви не имел в виду неуважение, когда удалил ваш ответ; Stack Overflow предназначен для меня как сайт вопросов и ответов, а не как форум. Вы можете задавать соответствующие дополнительные вопросы или оставлять комментарии (как будто это не компилируется :), щелкнув ссылку добавления комментария под соответствующим ответом.   -  person Stephen Cleary    schedule 28.09.2011


Ответы (1)


Обновление: моя библиотека Nito.AsyncEx теперь включает тип TeamFoundationClientAsyncFactory, который можно использовать вместо него. прокатки собственной реализации ниже.


API TFS не следует строго шаблону 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);
  }
}
person Stephen Cleary    schedule 22.09.2011
comment
Стивен, вы пробовали, как они работают с несколькими запросами / действиями workitemstore, происходящими одновременно с одного и того же клиента? MS упоминает, что каждый поток должен открывать свой экземпляр WorkItemStore в многопоточных сценариях (msdn .microsoft.com/en-us/library/bb130347.aspx). - person Jörg Battermann; 04.02.2012
comment
У меня нет доступного сервера TFS (мы используем SVN или Hg). Мой код просто предоставляет Task оболочку для существующих ICancelableAsyncResult API, поэтому ограничения потоков будут идентичными. В документе MSDN, на который вы ссылаетесь, говорится о многопоточности, но не о параллельных операциях из одного потока. - person Stephen Cleary; 04.02.2012
comment
Да, параллелизм! = многопоточность, но я имел в виду, что если базовый TaskScheduler действительно будет использовать отдельные потоки ... тогда это «может» быть проблемой. Нужно копнуть немного глубже, сравнить и посмотреть, что произойдет. - person Jörg Battermann; 05.02.2012