Show progress while restoring database in linqpad with appropriate extensions

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, it always gets the output of the handlers first (if I uncomment them).

Then first Linqpad shows the "Live Observables" results tab with

  • "Complete" observable, already completed and final message.
  • "PercentComplete" still pending and with "-" (no entries yet?)

I just want to get away from events using reactive extensions. First, the observable "PercentComplete" observable needs to be updated with real progress. Then "Complete" with the final message.

Question: how to properly configure the observed data?

+1


source to share


2 answers


My guess is that DumpLive () uses a dispatcher on the main thread.

Are you running a database restore on the main thread, or are you blocking the main thread waiting for the completion signal?

I experimented a little and started the Observable on a background thread using DumpLive () - if the main thread was blocked (for example using Wait or Thread.Sleep), the UI was not updated.

Even if it's on a different thread, it seems DumpLive () can only update the UI on the main thread. My guess is that it should render the Dispatcher updates related to the main thread.



When using DumpLive, LINQPad doesn't stop execution until the observables are finished, so if you have any blocking code in the main thread, remove it and see if it works then.

Here is my experimental code. Once you add to the sentence DumpLive()

in the example below, you will see the blocking behavior:

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;
    }
}

      

+2


source


The LINQPad method renders DumpLive

using WPF, so it cannot work if the main thread is blocked.

Instead, you can write your own version of DumpLive that renders HTML. It will be slower (because it has to update the HTML DOM every time the observable messages matter), but it will work regardless of whether the main thread is blocked.

Here is the code:



IDisposable DumpLatest<T> (IObservable<T> obs)
{
    var dc = new DumpContainer ().Dump();
    var extensionToken = Util.GetQueryLifeExtensionToken();
    return obs.Subscribe (
        value => dc.Content = value,
        ex => { dc.Content = ex; extensionToken.Dispose(); },
        () => extensionToken.Dispose());
}

      

If you define it as an extension method in My Extensions , you can call it when you need it.

+3


source







All Articles