New Posts New Posts RSS Feed: Blocking Async Queries
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Blocking Async Queries

 Post Reply Post Reply
Author
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Topic: Blocking Async Queries
    Posted: 10-Mar-2009 at 1:46pm
Hi,
 
We're writing a Silverlight application using DevForceEF.  We've a number of properties on our classes that are 'derived', meaning they're not directly bound to a column in the database, and they are computed on-the-fly.  Here's a bad example of the type of thing I'm talking about:
 
public partial class Employee
{
   public bool WorksAlone
   {
      get
      {
         MyEntityManager em = this.EntityManager as MyEntityManager;
         int count = em.Employees.Where(i => i.Location == this.Location).Count();
         return (count == 1);
      }
   }
}
 
The problem is that Silverlight requires the queries be executed asyncronously, which is fine in most cases, but with respect to a derived property like the example above, it becomes tricky.  Typically we'd use an IAsyncResult and simply wait until the Async call completed before returning the result, but I can't seem to figure out how to do this with DevForce.  The only thing I can seem to make work at all feels really dirty.
 
public partial class Employee
{
   public bool WorksAlone
   {
      get
      {
         bool result = false;
         MyEntityManager em = this.EntityManager as MyEntityManager;
         if (em != null)
         {
            IEntityQuery<Employee> query = em.Emplyoees.Where(i => i.Location == this.Location);
 
            worksAloneCompleted = false;
            em.ExecuteQueryAsync(query, new AsyncCompletedCallback<EntityFetchedEventArgs>(WorksAloneCompleted), null);
            while (!worksAloneCompleted);
 
            result = worksAloneResult;
         }

         return result;
      }
   }
 
   private bool worksAloneCompleted = false;
   private bool worksAloneResult = false;
   private void WorksAloneCompleted(EntityFetchedEventArgs args)
   {
      int count = 0;
      foreach (var item in args.Result)
         count++;
 
      worksAloneResult = (count == 1);
      worksAloneCompleted = true;
   }
}
 
Do you have any other recommendations how to handle this?
 
Thanks,
Ken
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 10-Mar-2009 at 5:06pm
Hi,

Could you do something similar to this:

    private bool mComputedPropertyInitialized;
    private int mComputedProperty = -1;
   
    public int ComputedProperty {
        get {
            if (mComputedPropertyInitialized) {
                return mComputedProperty;
            }
           
            EntityManager em = this.EntityManager;
            EntityQuery<User> query = new EntityQuery<User>();
            em.ExecuteQueryAsync(query, user_asyncCallback, null);
           
            return 0;
            //or another default value
        }
    }
    private void user_asynccallback(EntityFetchedEventArgs<User> pArgs)
    {
        //process result
        mComputedProperty = 1;
        mComputedPropertyInitialized = true;
        this.EntityAspect.ForcePropertyChanged(null);
    }

That way it will return your default until the async has been completed and then force rebinding with forcepropertychanged when it returns.
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 10-Mar-2009 at 10:55pm

Although maybe not appropriate here, when the situation allows it and the necessary data is already in cache you can set the QueryStrategy to CacheOnly and execute a synchronous query.  When things get messy with async queries, pre-loading can be a good idea.

DevForce also supports asynchronous navigation and returns a PendingEntity or PendingEntityList for navigation properties which must be fulfilled asynchronously.  Looks like this feature isn't yet in the Developer's Guide, but check out the Release Notes for more information (the feature was introduced in v4.2.0).
 
Also, be careful with any waits or sleeps or otherwise blocking the UI thread in Silverlight.   Something like
    while (!worksAloneCompleted);
could block the thread and leave the SL application hanging.
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 11-Mar-2009 at 3:03am
Hi smi-mark,
 
The trouble is, for our use case we're not binding to this property, we're using it in computations, and that computation has to be correct the first time through.  That, and the value can change frequently so performing the lazy load and storing a member variable doesn't work for us.  So, while in many cases your solution would likely be preferred, it's not really what we're looking to do.  Thanks though, I appreciate the feedback!


Edited by ken.nelson - 11-Mar-2009 at 3:05am
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 11-Mar-2009 at 3:08am
 
Originally posted by kimj

 
DevForce also supports asynchronous navigation and returns a PendingEntity or PendingEntityList for navigation properties which must be fulfilled asynchronously.  Looks like this feature isn't yet in the Developer's Guide, but check out the Release Notes for more information (the feature was introduced in v4.2.0).
 
 
I haven't looked into this yet, but hopefully it'll help, I'll update this post if it does.  Thanks.
 
Originally posted by kimj

 
Also, be careful with any waits or sleeps or otherwise blocking the UI thread in Silverlight.   Something like
    while (!worksAloneCompleted);
could block the thread and leave the SL application hanging.

Yeah, unfortunately it may be what we have to do.  This property has to return the correct value on each and every call.
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 11-Mar-2009 at 3:45am
Originally posted by kimj

DevForce also supports asynchronous navigation and returns a PendingEntity or PendingEntityList for navigation properties which must be fulfilled asynchronously.  Looks like this feature isn't yet in the Developer's Guide, but check out the Release Notes for more information (the feature was introduced in v4.2.0).

Also, be careful with any waits or sleeps or otherwise blocking the UI thread in Silverlight.   Something like
    while (!worksAloneCompleted);
could block the thread and leave the SL application hanging.
 
The PendingEntityList will definitely help for other properties, that's a good bit of info, can I suggest adding that to the developer's guide?
 
What actually looks like the fix I was looking for was also in the release notes for v4.2.0, so thanks for pointing me to that document:
 
For those needing additional control over their asynchronous operations the EntityManager also supports the IAsyncResult asynchronous pattern through an explicit implementation of the IEntityManagerAsync interface. You will need to cast an EntityManager to this interface in order to use methods following this pattern. In the IAsyncResult pattern an asynchronous operation is implemented as two methods named BeginOperationName and EndOperationName to begin and end the asynchronous operation "OperationName". More information on using this interface is available in the DevForce Framework Help reference.
 
This allows us to block the async in a much cleaner way.
 
IEntityQuery<Employee> query = em.Employee.Where(i => i.Location == this.Location);
IAsyncResult result = (em as IEntityManagerAsync).BeginExecuteQuery<Employee>(query, null, null);
while (!result.IsCompleted);
IEnumerable<Employee> x = (em as IEntityManagerAsync).EndExecuteQuery<Employee>(result);
 
return (x.Count() == 1);
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 11-Mar-2009 at 9:36am

Glad the IEntityManagerAsync might be useful.  Although, I'm still leery that these blocking calls will block the entire UI and effectively crash the browser.  Have you used them successfully in SL?

Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 12-Mar-2009 at 6:08am
Originally posted by kimj

Glad the IEntityManagerAsync might be useful.  Although, I'm still leery that these blocking calls will block the entire UI and effectively crash the browser.  Have you used them successfully in SL?

 
We've not yet tried the above solution in SL, our entire office is currently having some difficulty getting DevForce EF running properly using SL (See http://www.ideablade.com/forum/forum_posts.asp?TID=1127 if you're interested in that).  Once we can successfully run, I'll post an update.
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 12-Mar-2009 at 1:12pm
Ok, we've now got our machines working using SL, so I was finally able to test this, and you're right in that it locks up the browser, but I'm still confused as to why.  I would expect it to lock up for the period of time that it took to complete the async query, in which case, we'd work out some method of throwing up a progress updater, but it seems to just lock up entirely.  I guess we're just going to have to live with not having the derived properties.
 
One follow on question about Navigation Properties, we have a need to iterate over the collection that the navigation property would return, but obviously navigation properties are also async, so we can't really do that until the async completes.  I can't seem to figure out how to hook in a 'Completed' callback to a navigation property.  I tried doing something like the following but got a compile error:
 
IEntityQuery query = customer.Orders;
em.ExecuteQueryAsync(query, QueryCompleted, null);
 
I then tried the following, but got a runtime error:
 
IEntityQuery query = customer.Orders.ToQuery();
em.ExecuteQueryAsync(query, QueryCompleted, null);
 
The only way I could work it out was to do the following:
 
IEntityQuery query = em.Orders.Where(i => i.CustomerID == customer.UniqueID);
em.ExecuteQueryAsync(query, QueryCompleted, null);
 
Basically, I've got to ignore that a navigation property exists, and just control it manually.  Is there a better way to handle this?
 
Thanks,
Ken
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 12-Mar-2009 at 1:36pm
Is there no way you can wait till the derived property has populated and then call an event?

Ie, have a "wait" form in your SL app while it waits for the event to fire before continuing on?

Also, on your initial call you can do a span using .Include on the query

IEntityQuery query = em.Customers.Where(c => c.Id == 1).Include(Customer.OrdersEntityProperty.Name);

Then when the results come back you will have the orders.
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 12-Mar-2009 at 2:23pm
Ken,
 
All navigation in Silverlight will be done asynchronously if the data isn't already in cache.  There's a property on the EntityManager called UseAsyncNavigation which for SL is always on.  This means that something like
 
    customer.Orders
 
will automatically be resolved asynchronously if necessary.  You can use the IsPendingEntityList property on the RelatedEntityList to tell you whether "real" or "pending" data has been returned.
 
          var orders = customer.Orders;
          Assert.IsTrue(orders.IsPendingEntityList);
          orders.PendingEntityListResolved += (o, e) => {
             // Now have real data
             Assert.IsFalse(orders.IsPendingEntityList);
         }
 
For scalar properties you'd use IsPendingEntity and PendingEntityResolved.
 
It looks like we need to trap and disallow attempts to call ExecuteAsyncQuery on the navigation properties, since as you found this doesn't work. 
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 12-Mar-2009 at 2:27pm
Oh, regarding why the wait blocks the UI - you can take that up with Microsoft :-).  In order to avoid blocking the browser, they disallow any type of wait on the UI thread in Silverlight.  A little frustrating I know, since this same logic works fine in WPF and WinForms applications.
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 12-Mar-2009 at 2:31pm
If i need something right away i usuall use include but there are a few methods of doing it as Kim has said.

This gets the customer and the orders belong to the customer before returning.

          em.Customers.Where(c => c.Id ==1).Include(Customer.OrdersEntityProperty.Name).ExecuteAsync(
                args =>
                {
                    if (args.Error != null)
                    {
                        throw args.Error;
                    }
                    else
                    {
                        Customer cust = args.Result.First();
                        MessageBox.Show(cust.Orders.Count.ToString());
                    }
                },
                null);

This gets the customer, returns with the customer object, and then gets the orders.

         em.Customers.Where(c => c.Id == 1).ExecuteAsync(
          args =>
          {
              if (args.Error != null)
              {
                  throw args.Error;
              }
              else
              {
                  Customer cust = args.Result.First();
                  if (cust.Orders.IsPendingEntityList)
                  {
                      cust.Orders.PendingEntityListResolved += (o,args2) => {
                          MessageBox.Show(args2.ResolvedEntities.Count.ToString());
                      };
                  }
              }
          },
          null);
        }
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 13-Mar-2009 at 7:14am
Originally posted by smi-mark

Is there no way you can wait till the derived property has populated and then call an event?

Ie, have a "wait" form in your SL app while it waits for the event to fire before continuing on?
 
Probably, though it will take a bit of reworking our current app to do so.  Really a big part of the reason I posted this message was to determine what is and isn't possible, so this discussion has been a big help.  We'll just have to rethink the way we're doing certain things.
 
Originally posted by smi-mark


Also, on your initial call you can do a span using .Include on the query

IEntityQuery query = em.Customers.Where(c => c.Id == 1).Include(Customer.OrdersEntityProperty.Name);

Then when the results come back you will have the orders.
 
I agree that using .Include() should solve a lot of our problems, what I'm uncertain about with that though is the accuracy if multiple concurrent users are adding/removing customer orders and say the property on Customer is an Order count.  Any idea?
 
 
 
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 13-Mar-2009 at 7:17am
Originally posted by kimj

Ken,
 
All navigation in Silverlight will be done asynchronously if the data isn't already in cache.  There's a property on the EntityManager called UseAsyncNavigation which for SL is always on.  This means that something like
 
    customer.Orders
 
will automatically be resolved asynchronously if necessary.  You can use the IsPendingEntityList property on the RelatedEntityList to tell you whether "real" or "pending" data has been returned.
 
          var orders = customer.Orders;
          Assert.IsTrue(orders.IsPendingEntityList);
          orders.PendingEntityListResolved += (o, e) => {
             // Now have real data
             Assert.IsFalse(orders.IsPendingEntityList);
         }
 
For scalar properties you'd use IsPendingEntity and PendingEntityResolved.
 
That's exactly what I was looking for.  I'm not sure how I missed the PendingEntityListResolved delegate.  Thanks!
 
Back to Top
ken.nelson View Drop Down
Groupie
Groupie


Joined: 03-Mar-2009
Location: Ann Arbor, MI
Posts: 54
Post Options Post Options   Quote ken.nelson Quote  Post ReplyReply Direct Link To This Post Posted: 13-Mar-2009 at 7:20am
Originally posted by kimj

Oh, regarding why the wait blocks the UI - you can take that up with Microsoft :-).  In order to avoid blocking the browser, they disallow any type of wait on the UI thread in Silverlight.  A little frustrating I know, since this same logic works fine in WPF and WinForms applications.
 
Thanks. :)  I was more surprised than anything, like you stated, the same logic works fine in WPF and WinForms.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down