I have a fairly complex WPF application that (much like VS2013) has IDocuments
and ITools
docked within the main shell of the application. One of these Tools
needs to be shutdown safely when the main Window is closed to avoid getting into a "bad" state. So I use Caliburn Micro's public override void CanClose(Action<bool> callback)
method to perform some database updates etc. The problem I have is all of the update code in this method uses MongoDB Driver 2.0 and this stuff is async
. Some code; currently I am attempting to perform
public override void CanClose(Action<bool> callback)
{
if (BackTestCollection.Any(bt => bt.TestStatus == TestStatus.Running))
{
using (ManualResetEventSlim tareDownCompleted = new ManualResetEventSlim(false))
{
// Update running test.
Task.Run(async () =>
{
StatusMessage = "Stopping running backtest...";
await SaveBackTestEventsAsync(SelectedBackTest);
Log.Trace(String.Format(
"Shutdown requested: saved backtest \"{0}\" with events",
SelectedBackTest.Name));
this.source = new CancellationTokenSource();
this.token = this.source.Token;
var filter = Builders<BsonDocument>.Filter.Eq(
BackTestFields.ID, DocIdSerializer.Write(SelectedBackTest.Id));
var update = Builders<BsonDocument>.Update.Set(BackTestFields.STATUS, TestStatus.Cancelled);
IMongoDatabase database = client.GetDatabase(Constants.DatabaseMappings[Database.Backtests]);
await MongoDataService.UpdateAsync<BsonDocument>(
database, Constants.Backtests, filter, update, token);
Log.Trace(String.Format(
"Shutdown requested: updated backtest \"{0}\" status to \"Cancelled\"",
SelectedBackTest.Name));
}).ContinueWith(ant =>
{
StatusMessage = "Disposing backtest engine...";
if (engine != null)
engine.Dispose();
Log.Trace("Shutdown requested: disposed backtest engine successfully");
callback(true);
tareDownCompleted.Set();
});
tareDownCompleted.Wait();
}
}
}
Now, to start with I did not have the ManualResetEventSlim
and this would obviously return to the CanClose
caller before I updated my database on the background [thread-pool] thread. In an attempt to prevent the return until I have finished my updates I tried to block the return, but this freezes the UI thread and prevents anything from happening.
How can I get my clean-up code to run without returning to the caller too early?
Thank for your time.
Note, I cannot override the OnClose
method using async signature as the calling code would not await it (I have no control over this).
You can use something similar to WinForm's Application.DoEvents
but for WPF, it involves using a flag, firing your task, not Wait
ing for it, but continiously processing UI messages in a loop until your task is done and sets the flag. e.g.:
if (BackTestCollection.Any(bt => bt.TestStatus == TestStatus.Running))
{
bool done = false;
// Update running test.
Task.Run(async () =>
{
StatusMessage = "Stopping running backtest...";
await SaveBackTestEventsAsync(SelectedBackTest);
Log.Trace(String.Format(
"Shutdown requested: saved backtest \"{0}\" with events",
SelectedBackTest.Name));
this.source = new CancellationTokenSource();
this.token = this.source.Token;
var filter = Builders<BsonDocument>.Filter.Eq(
BackTestFields.ID, DocIdSerializer.Write(SelectedBackTest.Id));
var update = Builders<BsonDocument>.Update.Set(BackTestFields.STATUS, TestStatus.Cancelled);
IMongoDatabase database = client.GetDatabase(Constants.DatabaseMappings[Database.Backtests]);
await MongoDataService.UpdateAsync<BsonDocument>(
database, Constants.Backtests, filter, update, token);
Log.Trace(String.Format(
"Shutdown requested: updated backtest \"{0}\" status to \"Cancelled\"",
SelectedBackTest.Name));
StatusMessage = "Disposing backtest engine...";
if (engine != null)
engine.Dispose();
Log.Trace("Shutdown requested: disposed backtest engine successfully");
callback(true);
done = true;
});
while (!done)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}
}
It's a bit hacky, but given your situation and no control over the calling code, it might be your only option to maintain a responsive UI without immediately returning to the caller.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With