2

I'm trying to save app data when the app is closing/deactivating. In WP8 I used StorageFile, which only supports Async methods.

The problem is (as I suspected and confirmed when reading this article), simply stated, that the OS lifecycle events and async methods don't mix well together. So, this does not work (even without async/await)

private async void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
     var dataSvc = SimpleIoc.Default.GetInstance<ICachedDataService>();
     await dataSvc.StoreCachedDataAsync();
}

The article suggests 2 workaround, neither one seems ideal:

  1. Use a different API, e.g. IsolatedStorage instead of StorageFolder/File, which supports synchronous operations.
  2. Save-as-you-go vs. save in the end

My problem with (2) is that it still doesn't guarantee that it would have time to save even if I initiate it as soon as possible.

My problem with (1) is... beh... I'm using a ServiceLocator/IoC pattern (I could never remember which pattern is what), so this forces me to introduce synchronous operations in the interface of ICachedDataProvider, for example.

Is there any other approach? Is it possible to convert an Async method into a synchronous method to increase code reuse?

New Dev
  • 48,427
  • 12
  • 87
  • 129

3 Answers3

2

Is it possible to convert an Async method into a synchronous method to increase code reuse?

Unfortunately, no. There are various approaches you can take to wrapping those methods, but none of them are foolproof. This blog entry describes the various approaches. In your case, I would recommend offloading to another thread; it's the easiest to get working, but would only work if ICachedDataProvider is threadsafe.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • "This blog entry" => 403 forbidden – Jeroen van Langen Apr 07 '21 at 10:03
  • MS changed all their blog urls and didn't forward old ones... There's a [more exhaustive article](https://learn.microsoft.com/en-us/archive/msdn-magazine/2015/july/async-programming-brownfield-async-development) available, though. – Stephen Cleary Apr 07 '21 at 12:55
1

I found something that seems to work, but I'm going to let this question sit for a while to wait for feedback or other solutions.

I modified the Application_Closing and Application_Deactivated to the following (based on this SO question)

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
     var dataSvc = SimpleIoc.Default.GetInstance<ICachedDataService>();
     var task = Task.Run(async () => { await dataSvc.StoreCachedDataAsync(); });
     task.Wait();
}
Community
  • 1
  • 1
New Dev
  • 48,427
  • 12
  • 87
  • 129
  • That is the "offloading to another thread" option I recommended in my answer. If your `ICachedDataService` is threadsafe, this is the easiest solution. – Stephen Cleary Aug 03 '13 at 16:01
  • @StephenCleary, Ah... great... I didn't see your answer before posting mine. – New Dev Aug 03 '13 at 16:03
0

and thanks for the citation. As you found, there is the option of wrapping / kicking off the task and trying to synchronously wait for its completion.

But...this is a stopgap, at best. Once a close or deactivation operation is initiated, there is nothing that can stop it from happening, and nothing can defeat the "death clock" that gets started. Period. It is worth noting that the same applies to Suspensions in Windows Store apps. There is no mechanism to "buy more time" - Windows Store Apps do offer "deferalls", but as I mentioned in the post, those still operate within the constraint of the afore-mentioned "death clock."

I bring this up because you mentioned:

My problem with (2) is that it still doesn't guarantee that it would have time to save even if I initiate it as soon as possible.

As I read the concern, perhaps it is indicating a scenario where the app starts a "save as you go" operation, and then while the save is running, a lifetime event (close/deactivate) occurs...these lifetime events will be blissfully ignorant of the ongoing save and the app will ceded its execution and things will simply grind to an un-polite halt. In such a scenario, the save operation could include a synchronization flag to indicate that the operation is in progress, which could then be watched for inside of the lifetime event, but (at the risk of beating a dead horse) you still need to be aware of the whole "death clock" thing (plus things are starting to get complex/convoluted.)

Ultimately, if your app finds itself in a situation where a save may take longer than the allowable time, it may be important to rethink the exact mechanics at play and see if there's some way to avoid that situation in the first place. Although there are many ways to "play around" with asynchrony inside of this expiration time, expiration will happen.

John Garland
  • 1,291
  • 7
  • 8
  • I wasn't so much concerned about the death clock in (2), as I was with the async operation running while the OS lifecycle event happening. Yeah, if allowable time is not enough, you need to rethink your app and maybe require a background task (or something) – New Dev Aug 04 '13 at 03:58