Tuesday, December 11, 2012

Using TimeTrigger in Windows Store Apps

Using push notfications is all good and great, but many times you do not have resources to do so and just want some simple code to run from time to time to update your app's status. In my case, a pending document count.

For this purpose, you can create background tasks and add a TimeTrigger that can be executed as often as every 15 minutes.

Basically, you register your task like so:

// find out if task is already running

var existingTask = BackgroundTaskRegistration.AllTasks.Values.FirstOrDefault(
      t => t.Name.Equals(
      Constants.TILE_UPDATER_TASK_NAME,
      StringComparison.CurrentCultureIgnoreCase));

if (existingTask != null)
   return;    

// no task exists, create and register task
var taskBuilder = new BackgroundTaskBuilder
                  {
                     Name = Constants.TILE_UPDATER_TASK_NAME,
                     TaskEntryPoint =   
                     typeof(ReadSoft.Online.Approve.BackgroundTasks.TileUpdater).FullName
                   };

// specify when to invoke: every n minutes, when internet is present
taskBuilder.SetTrigger(new TimeTrigger(15 , false));
taskBuilder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable));
           
// register
var updaterTask = taskBuilder.Register();

Need more info? Read Registering a background task and Run background task on a timer


"Ok, great, but it's not working. Now what?"
Reading the links above, it's stated that in order for any background task to run, you need to also specify the class in the app manifest.



"Ok, great, but it's not working. Now what?"
Also stated in the MS documentation is that in order for a TimeTrigger to work, your app needs to be on the lock screen. You can request this on startup by calling RequestAccessAsync on the BackgroundExecutionManager. Note the try catch since you might have problems in the simulator with this (as did I), like so:

private async void RequestLockScreenAccess()
{
  try
  {
    var status = await BackgroundExecutionManager.RequestAccessAsync();
   }
   catch (Exception)
   {
   }
}

You can ofcourse handle the result (the user might say No) how you would like to, perhaps show a nagging screen explaining how important for the world peace it is that your app is on the freakin lock screen. Would someone pleeease think of the children!!  :)


"Ok, great, but it's not working. Now what?"
Not very visible in the documentation (it is mentioned in a white paper, but I actually got the info from a MS evangelist) is that your background class needs to be in a separate WinMD project. Do the rightclicky thing and add a new project pronto! Don't forget to update your app manifest with the (full) name of the class inside the WinMD instead of the one in your app.

Ok, but you've got all that nice code that checks with your webservice for status that you are also using inside the app, could you just put all that code in the WinMD project and then reference that from your app? No can do. Putting service references stuff in a WinMD doesn't play well because of generated classes not being public and other annoying things.

You need to put your reusable code in yet another project, but just a normal class library this time. Then, reference that lib from both your WinMD project and your app project and boom you're done.


"Ok, great, but it's not working. Now what?"
Running in the simulator? Try a real device instead.

OperationContextScope and async/await

So, we're doing a Windows 8 app. Yay :)
Being a c# and Silverlight kinda guy, the time needed to get up and running was incredibly small. As always, Microsoft are very good on the Tool-side of things and the "F5" experience works very well in the Windows 8 area.

Our services are WCF based, and we support both REST and SOAP.
In this case, we decided to use the SOAP way since our REST client helper library did not exist for Win8.
"Add New Service Reference" and boom we got ourselves some client code.

To make this work though, we had to put some time into the connectivity/authorization part.
We use a cookie to store authentication token (you know, standard ASP.NET authorization) and we needed  to pass along that cookie with other calls. We found that the best way to do so was to use the OperationContextScope and a previously saved cookiecontainer from the authorization call.


// prepares a soap client with the stored cookie container
var docClient = CreateDocumentServiceClient();

// call webservice
using (new OperationContextScope(docClient.InnerChannel))
{
   var doc = docClient.GetDocumentAsync(docId);
}


(...not gonna go into details of how to store a cookiecontainer etc, but I'm sure your friend Google could aid you if needed.)

In many of our calls, we needed to check something after the async call, so we started using await


private async void DoStuffWithDoc()
{
  var docClient = CreateDocumentServiceClient();  
  using (new OperationContextScope(docClient.InnerChannel))
  {
     await var doc = docClient.GetDocumentAsync(docId);
     if (doc.YadaYada)
     {
          // more code here
     }
  }
}


This mostly worked fine, but after a while we started noticing Exceptions, especially when doing other things in parallel. Often, it was "This OperationContextScope is being disposed on a different thread than it was created". When looking at the documentation for OperationContextScope it states that:
Caution noteCaution
Do not use the asynchronous “await” pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call “await” for an async call, use it outside of the OperationContextScope block


Aha! So, how do you solve this?
The await keyword will split up your code and basically create a new method of all the code after the await keyword (and that "new method" will be executed in a separate thread sometimes). Because the end of the using statement is after the await keyword, the Dispose of the OperationContextScope instance will take place in the "new method" that might run in a separate thread.

The solution is very easy, just make sure you do not use await inside a OperationContextScope. Duh!

So, all of our methods doing any kind of webservice call with an OperationContextScope needed to be refactored looking something like this:


private async void DoStuffWithDoc(string docId)
{
   var doc = await GetDocumentAsync(docId);
   if (doc.YadaYada)
   {
        // more code here
   }
}

public Task<Document> GetDocumentAsync(string docId)
{
  var docClient = CreateDocumentServiceClient();
  using (new OperationContextScope(docClient.InnerChannel))
  {
    return docClient.GetDocumentAsync(docId);
  }
}


See what we did there?
The await keyword has been moved outside the OperationContextScope to the calling method instead, and we immediately return after the call using the OperationContextScope.

Problem solved!