New Posts New Posts RSS Feed: IEntityManagerAsync EndExecuteQuery Return Value
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

IEntityManagerAsync EndExecuteQuery Return Value

 Post Reply Post Reply
Author
JoeGershgorin View Drop Down
Newbie
Newbie


Joined: 24-Feb-2010
Location: USA
Posts: 14
Post Options Post Options   Quote JoeGershgorin Quote  Post ReplyReply Direct Link To This Post Topic: IEntityManagerAsync EndExecuteQuery Return Value
    Posted: 20-Apr-2010 at 11:00am
I would like to leverage Devforce's IAsyncResult form of asynchronous query execution via the IEntityManagerAsync interface's BeginExecuteQuery and EndExecuteQuery methods (so it can be used with Wintellect's Powerthreading Asyncenumerator library without a messy wrapper).

However the IEnumerable entities returned by the EndExecuteQuery is not nearly as useful as the EntityFetchedEventArgs returned by ExecuteQueryAsync method of the EntityManager.

For instance how do I find if a query has been canceled when using the EndExecuteQuery?

Thanks for any information you can provide.
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 22-Apr-2010 at 1:44pm

Cancel information is not part of the IAsyncResult interface ... defined by Microsoft ... and the concrete type that implements it (AsyncResult) does not reveal more than the interface allows.

Avoid this Begin/End approach where possible. Avoid it for certain in build 6.0.1 as it is not very useful and has a serious defect. We have recorded a request for repairs and improvements.
 
*** Editorial Remarks ***
 
We did not intend this interface to be "as useful" as the other async query approaches.  This Async pattern is not recommended for most developers, by us nor by Microsoft, and that's why we oblige you to cast the EntityManager to IEntityManagerAsync to use it.
 
I wonder why you are using Wintellect's AsyncEnumerator when you have the more powerful DevForce AsyncSerialTask at your disposal. For example, the IAsyncResult of a Begin/End pattern does not expose exceptions which makes it hard to manage trouble.
 
If you insist (and I caution you again that you should prefer AsyncSerialTask), you will have to write the wrapper.
 
 
Back to Top
JoeGershgorin View Drop Down
Newbie
Newbie


Joined: 24-Feb-2010
Location: USA
Posts: 14
Post Options Post Options   Quote JoeGershgorin Quote  Post ReplyReply Direct Link To This Post Posted: 22-Apr-2010 at 4:06pm
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).

http://www.bitspy.com/QueryAsIAsyncResult.cs

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()[0];           
                         
    // 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
    ae.DequeueAsyncResult();
    ae.DequeueAsyncResult();
       
    IsBusy = false;
  
    // and finally, we can't leave the iterator "hanging" - we need
    // to let it know we're done
    yield break;
}



Edited by JoeGershgorin - 22-Apr-2010 at 4:10pm
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 22-Apr-2010 at 5:06pm
As you wish, Joe. 
 
I don't like "yield result 2" for embedding parallel tasks (you mean I have to count? Oh boy).
 
I don't see any exception handling code in your example; what does that do to the complexity?
 
Every time I write with the AsyncTasks I get something crisper and clearer than what you have here ... without the yields that most of our customers are going to have a heck of a time groking. And you don't need to do mapping in order to process results.
 
But why argue? Yours is a perfectly reasonable approach.
 
I agree completely that our 6.0.1 implementation of IAsyncResult stinks. Can I be more clear? We can improve it. We will improve it. And I'm using this thread as motivation.
 
Microsoft explains its preference for the Event-based async pattern here: http://msdn.microsoft.com/en-us/library/ms228966.aspx
 
RIA Services also directs folks toward the Event-based pattern.
 
We should support all persuasions if we can do so without confusing people or leading them down the wrong path. We will.
 
Thanks for the feedback.
Back to Top
JoeGershgorin View Drop Down
Newbie
Newbie


Joined: 24-Feb-2010
Location: USA
Posts: 14
Post Options Post Options   Quote JoeGershgorin Quote  Post ReplyReply Direct Link To This Post Posted: 22-Apr-2010 at 5:49pm
Thanks for the thoughts.

I kind of like the count, makes it explicit how many tasks I'm yielding for. You could also queue up an a IEnumerable of async requests and just yield the IEnumerable count if you don't like counting.

AsyncEmurator certainly supports exception handling, early cancellation of the workflow, time-outs and many other features, they're just not in my sample.

On the 15th of April Richter blogged about being in talks with Microsoft in including AsyncEnumerator in the next .NET framework, so you may see eventually Asyncenumerator as a first class language citizen.

Microsoft also seems to have be interested in handling async requests via the reactive extensions, but wrapping my head around observables requests managed by LINQ queries gives me a headache (and so does the code people have created that emulates co-routines using RX).

More info here:
http://msdn.microsoft.com/en-us/magazine/cc163323.aspx
http://csharperimage.jeremylikness.com/2010/03/sequential-asynchronous-workflows-in.html
http://csharperimage.jeremylikness.com/2010/03/sequential-asynchronous-workflows-part.html
http://www.wintellect.com/PowerThreading.aspx



From the first link here's a few things to note when using try/catch blocks in an IEnumerator:
  • An iterator member's signature cannot contain any out or ref parameters.
  • An iterator cannot contain a return statement (yield return is OK).
  • An iterator may not contain any unsafe code.
  • A finally block may not contain a yield return or a yield break statement.
  • A yield return statement may not appear in a try block that has a catch block (yield break is OK).
  • A yield return statement may not appear in a catch block (yield break is OK).
Yielding is a lot more clear to me than dealing with mapping when using AsyncTasks, for example (how can mapping be avoided?):

Repository.Query(Mass.Persistence.QueryNames.LoggedInStaff,
                           delegate(IEnumerable results)
                           {
                               _loggedInStaff = results.Cast<Staff>().ToList()[0];

                               var task = AsyncParallelTask.Create();
                              
                               task.AddAsyncQuery(1, x => Repository.GetQuery<LeadStatus>().OrderBy(ls => ls.Description));
                               task.AddAsyncQuery(2, x => Repository.GetQuery<LeadSource>().OrderBy(ls => ls.Description));
                               task.AddAsyncQuery(3, x => Repository.GetQuery<MktSource>().OrderBy(ms => ms.Description));
                               task.AddAsyncQuery(4, x => Repository.GetQuery<DigitalAddressType>().OrderBy(da => da.Description));
                               task.AddAsyncQuery(5, x => Repository.GetQuery<CorporateGroup>().OrderBy(cg => cg.Description));
                               task.AddAsyncQuery(6, x => Repository.GetQuery<Club>(Mass.Persistence.QueryNames.AuthorizedClubs, _loggedInStaff).OrderBy(c => c.ClubDescription));
                               task.AddAsyncQuery(7, x => Repository.GetQuery<Staff>(Mass.Persistence.QueryNames.AssociatedClubsStaff, _loggedInStaff).OrderBy(s => s.User.Person.LastName));
                               task.Execute(delegate(AsyncParallelTaskCompletedArgs cb)
                               {                                 
                                   var q1Result = ((EntityFetchedEventArgs<LeadStatus>)cb.CompletionMap[1]);
                                   var q2Result = ((EntityFetchedEventArgs<LeadSource>)cb.CompletionMap[2]);
                                   var q3Result = ((EntityFetchedEventArgs<MktSource>)cb.CompletionMap[3]);
                                   var q4Result = ((EntityFetchedEventArgs<DigitalAddressType>)cb.CompletionMap[4]);
                                   var q5Result = ((EntityFetchedEventArgs<CorporateGroup>)cb.CompletionMap[5]);
                                   var q6Result = ((EntityFetchedEventArgs<Club>)cb.CompletionMap[6]);
                                   var q7Result = ((EntityFetchedEventArgs<Staff>)cb.CompletionMap[7]);
                                  
                                   LeadStatusList = new ObservableCollection<LeadStatus>(q1Result.Result.ToList());
                                   LeadSourceList = new ObservableCollection<LeadSource>(q2Result.Result.ToList());
                                   MktSourceList = new ObservableCollection<MktSource>(q3Result.Result.ToList());
                                   DigitalAddressTypeList = new ObservableCollection<DigitalAddressType>(q4Result.Result.ToList());
                                   CorporateGroupList = new ObservableCollection<CorporateGroup>(q5Result.Result.ToList());
                                   ClubList = new ObservableCollection<Club>(q6Result.Result.ToList());
                                   StaffList = new ObservableCollection<Staff>(q7Result.Result.ToList());
                                   IsBusy = false;
                               });
                           });


is a little uglier to me, and mix and matching serial and parallel is done via delegate nesting which can get real ugly real fast.








Edited by JoeGershgorin - 22-Apr-2010 at 5:58pm
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 22-Apr-2010 at 7:27pm
You don't need the mapping. You missed an important DevForce overload of that signature.
 
For example: 
task.AddAsyncQuery(
  null,
   x => Repository.GetQuery<LeadStatus>().OrderBy(ls => ls.Description),
   op => LeadStatusList = new ObservableCollection<LeadStatus>(op.Results)
);
Btw, I would refactor to remove the awful repetitions of "Repository.GetQuery<T>().OrderBy(ls => ls.Description)," and "LeadStatusList = new ObservableCollection<LeadStatus>(op.Results)"
 
I'd also add extension method* to eliminate unneeded null key and use each list to determine both the query and the dispostion of it.  The implementation leverages the fragment above but would read like this.
task.QueryForLists(
   LeadStatusList,
   LeadSourceList,
   MktSourceList,
   DigitalAddressTypeList,
   CorporateGroupList);
 
       * or just a regular method that took "task" as its first argument
 
Now admit it ... isn't that a lot more intention revealing than the earlier attempts?
 
If I thought about it more I could probably do the last  omitted two as well simply by registering the default ordering of the Club and Staff types somewhere ... or sorting them client-side.
 
I am more hopeful about Rx than you. I figure we'll get over the headaches once we get used to IObservable ... just as we did with lambdas and LINQ which were equally as mystifying at first.
 
Thanks for the references though!  Valuable. I really do like the CoRoutine stuff.
 
Back to Top
JoeGershgorin View Drop Down
Newbie
Newbie


Joined: 24-Feb-2010
Location: USA
Posts: 14
Post Options Post Options   Quote JoeGershgorin Quote  Post ReplyReply Direct Link To This Post Posted: 22-Apr-2010 at 10:25pm
Cool, useful overload, thanks!

The AddAsyncQuery parallel execution example I posted above is not equivalent to the Coroutine example. I would like to see how a combination of Parrallel and Serial Devforce Async calls would look in combination the way the Coroutine sample works above. I think some of it also comes to personal preference because I still find the Coroutine version easier to flow through, inline delegate are very nice, if a bit ugly, and nested delegates I find requires way too much thought to follow the flow (a few coworkers agree).

I could certainly use extensions, reflection, and conventions to minimize code in many scenarios, but honestly the extensions would have applicability in maybe 2-3 instances. Plus there is some uniquess there, the last two queries are taking in input parameter(s) that can change depending on context. In several cases I also have to post process the queries to add them into proxy objects to support "Show All" , "None", etc.. ComboBox list items.

It's more code to specify the Repository methods, but it's still a single line fairly short statement and a ton more self documenting.

The OrderBy in the above code is just some inherited code I didn't write, now that I look at it, it should be factored out into the Repository IEntityQuery definition. I try to handle as much LINQ on the server side as possible so as not to take a hit on the UI thread (without having to create an explicit thread just for ordering/additional filters).

On the topic of RX, you probably already know about this, but it just hit my radar, the following is a great site for learning RX, done in the style of 101 Linq:

http://rxwiki.wikidot.com/101samples

And a few interesting discussions concerning Coroutines and how to do them in RX:

http://social.msdn.microsoft.com/Forums/en-NZ/rx/thread/dfabe9e5-6f16-4b0c-9b52-53f6f6d5a202
http://social.msdn.microsoft.com/Forums/en/rx/thread/c51ef077-4433-4308-9c3d-fcafc90905da





Edited by JoeGershgorin - 22-Apr-2010 at 10:32pm
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down