I have the following C# code.
var databaseRestore = new Microsoft.SqlServer.Management.Smo.Restore();
//databaseRestore.PercentComplete += CompletionStatusInPercent;
//databaseRestore.PercentCompleteNotification = 10;
//databaseRestore.Complete += Restore_Completed;
...
var complete = Observable
.FromEventPattern(databaseRestore, "Complete")
.Select(x=>x.EventArgs as ServerMessageEventArgs)
.Select(x=>x.Error.Message)
.Take(1)
.DumpLive("Complete");
var percentComplete = Observable
.FromEventPattern(databaseRestore, "PercentComplete")
.Select(x=>x.EventArgs as PercentCompleteEventArgs)
.Select(x=>x.Percent)
.TakeUntil(complete)
.DumpLive("PercentComplete");
...
databaseRestore.SqlRestore(server);
If I run it this, first comes always the output from handlers (if I uncomment them).
Then first, Linqpad shows the "Live Observables" result tab with
What I want is just to come away from events using reactive extenxions. First should come the "PercentComplete" observable updated with actual progress. Then "Complete" with final message.
The question: how do I set up the observables properly?
My guess is this is the fault of how DumpLive() uses a Dispatcher on the main thread.
Are you running the database restore on the main thread, or blocking the main thread waiting for a completion signal?
I experimented a bit and ran an Observable on a background thread with a DumpLive() - if the main thread was blocked (e.g. with a Wait or a Thread.Sleep) the UI did not update.
Even if it's on a different thread, it seems DumpLive() can only update the UI on the main thread. My guess it that it needs to render updates on a Dispatcher associated with the main thread.
When using DumpLive LINQPad won't terminate execution until the observables complete, so if you have any blocking code on main thread remove it and see if it works then.
Here's my experimental code. As soon as you add in either DumpLive()
clause in the example below, you'll see the blocking behaviour:
void Main()
{
Task.Run(() => {
var progressor = new Progressor();
var complete = Observable
.FromEventPattern(
h => progressor.Complete += h,
h => progressor.Complete -= h)
.Select(_ => "Complete")
.Take(1);
// append this to see blocking
//.DumpLive();
complete.Subscribe(Console.WriteLine);
var percentComplete = Observable
.FromEventPattern<PercentCompleteEventArgs>(
h => progressor.PercentComplete += h,
h => progressor.PercentComplete -= h)
.TakeUntil(complete)
.Select(i => "PercentComplete " + i.EventArgs.PercentComplete);
// append this to see blocking
// .DumpLive();
percentComplete.Subscribe(Console.WriteLine);
});
Thread.Sleep(5000);
}
public class Progressor
{
public Progressor()
{
Observable.Interval(TimeSpan.FromSeconds(1)).Take(10)
.Subscribe(
i => RaisePercentComplete(((int)i+1) * 10),
() => RaiseComplete());
}
private void RaiseComplete()
{
var temp = Complete;
if(temp != null)
{
temp(this, EventArgs.Empty);
}
}
private void RaisePercentComplete(int percent)
{
var temp = PercentComplete;
if(temp != null)
{
temp(this, new PercentCompleteEventArgs(percent));
}
}
public event EventHandler Complete;
public event EventHandler<PercentCompleteEventArgs> PercentComplete;
}
public class PercentCompleteEventArgs : EventArgs
{
public int PercentComplete { get; private set; }
public PercentCompleteEventArgs(int percent)
{
PercentComplete = percent;
}
}