This project has moved. For the latest updates, please go here.

Synchronous refresh (for updating live tiles from background agent)

Dec 9, 2011 at 7:11 PM

I have an app that uses a background agent to update some secondary tiles that display data from a webservice. I put my Agfx models/etc in a shared class library that my background agent and app can both access. It works fine in theory, but now I have run into an issue that may be a feature request.

When the background agent runs, I instantiate my Agfx models and can then call Refresh() to force a call to the webservice. However, Refresh() is asynchronous (as it should be), so I cant easily 'wait' for the refresh to complete so that I can update my tiles with the new data. I could use some waithandles and all that, but I wonder if there is a better way, or if Agfx could implement some kind of synchronous Refresh()?

Or maybe I am coming at this from the wrong angle - any thoughts?

Dec 9, 2011 at 7:30 PM

Actually, using Agfx from the background agent doesnt work at all. Lots of 'invalid-cross thread access' exceptions since there is no UI thread. Seems like a natural extension to want to use the same data loading/caching logic in the app and background agent, so is there any advice around this scenario?

Dec 9, 2011 at 9:52 PM

Sorry for spamming this discussion, but I got to the root of the issue. When using BatchObservableCollections in my AgFx viewmodels that are updated from a background agent, the call to items.Clear() (as suggested by the AgFx documentation on how to write the setter for collection properties) was throwing the exception. The cause was that the CancelBatch() method was trying to stop the _timer, but since it was a DispatchTimer, it was throwing an 'illegal cross-thread access' exception. I noticed that you already had an IsUIThread() method in there, so the fix for me was to check that value in the BatchObservableCollection's constructor and to not even instantiate the _timer variable if it was not on the UI thread:

if (_batchSize > 0)

becomes:

if (_batchSize > 0 && IsUiThread)

Everywhere else in that class that _timer is accessed, there is a null check, so simply not creating the timer seemed like the best approach. Once I did that, the exception went away and my viewmodels were able to be updated from my background agent.

Thanks for making this library available - it is a definite time-saver.

Coordinator
Dec 9, 2011 at 11:48 PM

Hi Brian -

You know, I'll just come clean and say I've never tried using AgFx from a background agent.  Sorry for the bug, but THANK YOU for finding it.

I'll add this fix plus some unit tests that run from a BG thread to test it.  BatchObservableCollection can be a bit difficult at times due to it's thread affinity.  Your fix makes sense.

Thanks,

Shawn

Coordinator
Dec 9, 2011 at 11:56 PM

Oh, on your original question, I think a wait handle is the way to go.  Every time I've trying baking any synchronous behavior into AgFx, I regret it. :)

Just wait on the callbacks from Refresh and you should be good to go.

Jan 7, 2012 at 10:41 PM
Edited Jan 7, 2012 at 11:29 PM

Could you help me? How would I wait for the Completed event to finished?

Thank you!

DataManager.Current.Load<MyModel>("id" , (vm) => { /* do something */ }, (vm) => { /* do nothing */ });

I want to use this in a BackgroundTask (PeriodicTask) but can't get my head around how I would be able to wait for Completed to be called before I continue and call NotifyCompleted()

 

UPDATE:

got it working

public Task<MyModel> GetLatest(){

TaskCompletionSource<MyModel> tcs = new TaskCompletionSource<MyModel>();

Action<Exception> d2 = (vm) => { tcs.TrySetResult(null); };

var a = DataManager.Current.Load<MyModel>( "votes", d1, d2);

Task.WaitAny(tcs.Task); return tcs.Task;

}

Action<MyModel> d1 = (vm) => { tcs.TrySetResult(vm); };