我有以下C#代码。
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);
如果我运行它,首先来自处理程序的输出(如果我取消注释它们)。
然后,首先,Linqpad显示带有
的“Live Observables”结果选项卡我想要的只是远离使用反应性extenxions的事件。 首先应该通过实际进度更新“PercentComplete”可观察量。 然后用“最终消息”“完成”。
问题:如何正确设置可观察量?
答案 0 :(得分:3)
LINQPad的DumpLive
方法使用WPF渲染,因此如果主线程被阻止,它就无法工作。
您可以编写自己的DumpLive版本,该版本使用HTML呈现。这将更慢(因为每次observable发布值时都必须更新HTML DOM),但无论主线程是否被阻止,它都会起作用。
以下是代码:
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());
}
如果您在我的扩展程序中将其定义为扩展程序方式,则可以在需要时随时调用它。
答案 1 :(得分:2)
我猜这是DumpLive()在主线程上使用Dispatcher的错误。
您是在主线程上运行数据库还原,还是阻塞主线程等待完成信号?
我尝试了一下并在后台线程上使用DumpLive()运行了一个Observable - 如果主线程被阻止(例如使用Wait或Thread.Sleep),UI没有更新。
即使它位于不同的线程上,似乎DumpLive()也只能更新主线程上的UI。我猜它需要在与主线程关联的Dispatcher上呈现更新。
使用DumpLive时,LINQPad在observable完成之前不会终止执行,所以如果主线程上有任何阻塞代码,请将其删除并查看是否有效。
这是我的实验代码。只要您在下面的示例中添加DumpLive()
子句,就会看到阻止行为:
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;
}
}