I find it surprising that Microsoft would state that it doesn't recommend the IAsyncResult pattern. The pattern is implemented for almost every async API in the framework itself. Things like webrequest and event delegates themselves (BeginInvoke and EndInvoke on the delegate type) implement the pattern.
IAsyncResult needs to be returned by the Begin method, but the implementation of what is returned by the end method is up to the implementer, it can be void or actual value. I just find it odd and incosistant that the IEntityManagerAsync returns just the IEnumerable result instead of a richer object with more information such as cancelation status of the query result that's included in EntityFetchedEventArgs.
I ended up creating my own wrapper agains the entity manager's ExecuteQueryAsync method. It turned out to be quite easy to use. I new it up in my PersitenceContext manager when the Entity manager gets created and since my calls are abstracted via my PersistenceContext and Repository wrappers all I had to do was change my wrapper signatures.
For those that may be interested, the wrapper is here (uses AsyncResult<T> object from Powerthreading lib).
AsyncEnumerator is co-routine implementation created by Jeffrey Richter made much earlier than the Caliburn implementation, and is actually richer/more powerfull. Richter also had a hand in the CCR version, which was the first co-routine implementation in .NET (he calls AsyncEnumrator a lite version of the CCR one).
I prefer AsyncEnumerator over Devforce's AsyncParallelTask and AsyncSerialTask because:
1.) Not a big fan of completion map plumbing code I have to write to map queries to results.
2.) Not so easy to mix and match serial and parallel queries without using nested delegates, which I'm trying to avoid for readability and maintainability.
3.) AsyncEnumerator can be used for batching any serial and/or parallel async requests in a single workflow.
Here's an example of an AsyncEnumerator workflow, which IMO is much easier to manage/read:
private IEnumerator<Int32> InitViewModel(AsyncEnumerator ae)
BusyText = "Loading...";
// Run the following query serially
Repository.BeginExecuteQuery<Staff>(QueryNames.LoggedInStaff, ae.End(), null);
// Yield until one query finishes (1 value in yield return)
yield return 1;
// process results of async query
// Since the EndExecuteQuery, via my wrapper, now returns a EntityFetchedEventArgs
// intead of IEnumerable results we can check for cancelation
// For example:
// if (Repository.EndExecuteQuery<Staff>(ae.DequeueAsyncResult()).Cancelled)
_loggedInStaff = Repository.EndExecuteQuery<Staff>(ae.DequeueAsyncResult()).Result.ToList();
// Run the following two queries in parallel
var arAssociatedClubsStaff = Repository.BeginExecuteQuery<Staff>(QueryNames.AssociatedClubsStaff, ae.End(), null, _loggedInStaff);
var arAuthorizedClubs = Repository.BeginExecuteQuery<Club>(QueryNames.AuthorizedClubs, ae.End(), null, _loggedInStaff);
// Yield until the two above queued up query to finishes (2 value in yield return)
yield return 2;
// Process the results
_associatedClubsStaffList = Repository.EndExecuteQuery<Staff>(arAssociatedClubsStaff).Result.ToList();
_authorizedClubsList = Repository.EndExecuteQuery<Club>(arAuthorizedClubs).Result.ToList();
// Clear the async queue inbox to prevent memory leaks
IsBusy = false;
// and finally, we can't leave the iterator "hanging" - we need
// to let it know we're done
Edited by JoeGershgorin - 22-Apr-2010 at 4:10pm