How to use AsyncCTP using the APM TFS method (Query.Begin / EndQuery)?

I would like to try using AsyncCTP with TFS. There is currently a long method that calls RunQuery on a Query TFS instance.

The query provides the APM BeginQuery () and EndQuery () methods. As far as I understand, the recommended approach for transferring them using AsyncCTP looks something like this: (example from the docs)

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

Also, wrap it in an extension method, as in the docs, so my actual method is as follows:

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

... but this does not compile. Getting an intellisense "wrong argument" error, which, frankly, I can’t understand, because the types and format look right. One possible problem may be that APM Query methods expect ICanceleableAsyncResult, while Task factory expects IAsyncResult, but looking at the TFS API, ICanceleableAsyncResult is a specialization of IAsyncResult.

Not sure if I am doing this wrong or simply impossible. I would like to be able to do this in the AsyncCTP way, but you may have to go back to the APM pattern - ugh!

+1
source share
1 answer

Update: My Nito.AsyncEx Library now includes a TeamFoundationClientAsyncFactory type that you can use instead, follow your own implementation below.


The TFS API does not strictly follow the APM pattern because it does not accept the state parameter, and this prevents the built-in TaskFactory.FromAsync from working.

You will have to write your own equivalent FromAsync , which can be done using 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); } } }; } } 

Then you can use it in extension methods as such:

 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); } } 
+4
source

All Articles