C# 缓冲来自数据库游标的数据,同时保持UI响应
我有一个填充的数据库目录和一个可用于检索对象的游标。这个目录显然非常大,我想做的是使用ReactiveUI缓冲数据,同时保持UI数据绑定和响应。我按照以下步骤将我的C# 缓冲来自数据库游标的数据,同时保持UI响应,c#,wpf,system.reactive,reactiveui,C#,Wpf,System.reactive,Reactiveui,我有一个填充的数据库目录和一个可用于检索对象的游标。这个目录显然非常大,我想做的是使用ReactiveUI缓冲数据,同时保持UI数据绑定和响应。我按照以下步骤将我的IEnumerable转换为IObservable,如下所示: public class CatalogService { ... public IObservable<DbObject> DataSource { get { return Obs
IEnumerable
转换为IObservable
,如下所示:
public class CatalogService
{
...
public IObservable<DbObject> DataSource
{
get
{
return Observable.Create<DbObject>(obs =>
{
var cursor = Database.Instance.GetAllObjects();
var status = cursor.MoveToFirst();
while (status == DbStatus.OK)
{
var dbObject= Db.Create(cursor);
obs.OnNext(dbObject);
status = cursor.MoveToNext();
}
obs.OnCompleted();
return Disposable.Empty;
});
}
}
}
不幸的是,结果恰恰相反。当我的视图控件(包含绑定到DbObjects
属性的简单ListBox
数据)加载时,在枚举整个目录之前,它不会显示任何数据。只有这样,UI才会刷新
我不熟悉ReactiveUI,但我确信它能够完成手头的任务。如果我不正确地使用它,有人有什么建议或建议吗?在等待进一步的信息之前,我猜您可能有几个零长度缓冲区,具体取决于DB查询所用的时间,然后是一个包含所有结果的非零长度缓冲区。您最好通过长度和时间来限制缓冲区大小 编辑-我只是想对原始实现中涉及的各种线程进行分析。我不同意Paul的分析,我不相信UI线程会因为DB查询而被阻塞。我相信它被阻止是因为大量的结果被缓冲了 Charlie-拜托,你能用代码计算DB查询的时间(不用调试器)并转储你得到的缓冲区长度吗 我将对代码进行注释,以显示所有三个线程的顺序: 首先,在提供的代码之外,我假设通过
Loaded
事件调用OnLoad
(1) -UI线程调用OnLoad
public ObservableCollection<DbObject> DbObjects { get; set; }
private async void OnLoad(object sender, RoutedEventArgs e)
{
// (2) UI Thread enters OnLoad
var observableData = CatalogService.Instance.DataSource.Publish();
var chunked = observableData
// (6) Thread A OnNext passes into Buffer
.Buffer(TimeSpan.FromMilliseconds(100));
// (7) Thread B, threadpool thread used by Buffer to run timer
var dispatcherObs = chunked
// (8) Thread B still
.ObserveOnDispatcher(DispatcherPriority.Background);
// (9) Non blocking OnNexts back to UI Thread
dispatcherObs.Subscribe(dbObjects =>
{
// (10) UI Thread receives buffered dbObjects
foreach (var dbObject in dbObjects)
{
// (11) UI Thread hurting while all these images are
// stuffed in the collection in one go - This is the issue I bet.
DbObjects.Add(dbObject);
}
});
await Task.Run(() =>
{
// (3) Thread A - a threadpool thread,
// triggers subscription to DataSource
// UI Thread is *NOT BLOCKED* due to await
observableData.Connect()
});
// (13) UI Thread - Dispatcher call back here at end of Create call
// BUT UI THREAD WAS NOT BLOCKED!!!
// (14) UI Thread - This task will be already completed
// It is causing a second subscription to the already completed published observable
await dispatcherObs.ToTask();
}
public class CatalogService
{
...
public IObservable<DbObject> DataSource
{
get
{
return Observable.Create<DbObject>(obs =>
{
// (4) Thread A runs Database query synchronously
var cursor = Database.Instance.GetAllObjects();
var status = cursor.MoveToFirst();
while (status == DbStatus.OK)
{
var dbObject= Db.Create(cursor);
// (5) Thread A call OnNext
obs.OnNext(dbObject);
status = cursor.MoveToNext();
}
obs.OnCompleted();
// (12) Thread A finally completes subscription due to Connect()
return Disposable.Empty;
});
}
}
}
publicobservableCollection DbObjects{get;set;}
私有异步void OnLoad(对象发送方,RoutedEventArgs e)
{
//(2)UI线程进入OnLoad
var observeData=CatalogService.Instance.DataSource.Publish();
var chunked=observedData
//(6)线程A OnNext进入缓冲区
.缓冲区(时间跨度从毫秒(100));
//(7)线程B,缓冲区用于运行计时器的线程池线程
var dispatcherObs=分块
//(8)螺纹B静止
.ObserveOnDispatcher(DispatcherPriority.Background);
//(9)非阻塞OnNexts返回UI线程
dispatcherObs.Subscribe(dbObjects=>
{
//(10)UI线程接收缓冲的DBObject
foreach(dbObjects中的var dbObject)
{
//(11)当所有这些图像都被删除时UI线程受损
//我敢打赌,这是一个问题。
添加(dbObject);
}
});
等待任务。运行(()=>
{
//(3)螺纹A-螺纹池螺纹,
//触发对数据源的订阅
//由于等待,UI线程*未被阻止*
observeData.Connect()
});
//(13)UI线程-调度程序在创建调用结束时在此回调
//但是UI线程没有被阻止!!!
//(14)UI线程-此任务将已完成
//这将导致对已完成的已发布的可观察对象进行第二次订阅
等待调度。ToTask();
}
公共类编目服务
{
...
公共IObservable数据源
{
得到
{
返回可观察的。创建(obs=>
{
//(4)线程A同步运行数据库查询
var cursor=Database.Instance.getAllObject();
var status=cursor.MoveToFirst();
while(status==DbStatus.OK)
{
var dbObject=Db.Create(游标);
//(5)在下一个线程上执行调用
obs.OnNext(dbObject);
status=cursor.MoveToNext();
}
obs.OnCompleted();
//(12)线程A由于Connect()最终完成订阅
返回一次性。空;
});
}
}
}
我认为问题是一个大的缓冲区一次将成吨的结果卸载到ObservableCollection中,为listbox创建了成吨的工作。您的问题在于:
while (status == DbStatus.OK)
{
var dbObject= Db.Create(cursor);
obs.OnNext(dbObject);
status = cursor.MoveToNext();
}
一旦有人订阅,该循环就会以阻塞方式同步运行。由于您在UI线程上创建订阅(在调用Connect时),它将在UI线程上运行整个过程。将其更改为:
return Observable.Create<DbObject>(obs =>
{
Observable.Start(() => {
var cursor = Database.Instance.GetAllObjects();
var status = cursor.MoveToFirst();
while (status == DbStatus.OK)
{
var dbObject= Db.Create(cursor);
obs.OnNext(dbObject);
status = cursor.MoveToNext();
}
obs.OnCompleted();
}, RxApp.TaskPoolScheduler);
return Disposable.Empty;
});
returnobservable.Create(obs=>
{
可观察。开始(()=>{
var cursor=Database.Instance.getAllObject();
var status=cursor.MoveToFirst();
while(status==DbStatus.OK)
{
var dbObject=Db.Create(游标);
obs.OnNext(dbObject);
status=cursor.MoveToNext();
}
obs.OnCompleted();
},RxApp.TaskPoolScheduler);
返回一次性。空;
});
问题:您是如何确定用户界面不仅仅是简单地呈现结果,如此之快以至于您无法看到结果的?用户界面实际上没有响应吗?在DB查询中,第一个结果和最后一个结果的时间是多少?结果集中有多少行?快速提问。。。这里什么东西是IEnumerable?@Christopher:没有什么是IEnumerable。DataSource属性已从IEnumerable转换为IObservable。很抱歉,我不想说IEnumerable。@James:UI实际上没有响应,但列表框一直是空的。大约有1000行(图像),列表框在相当长的一段时间后从0个图像变为所有图像。尝试限制项目数量,一直降到仅10个项目。同样的结果——列表框很长一段时间没有数据,最后所有的项目都出现了。我认为詹姆斯的理论仍然成立。与迭代查询结果相比,DB查询将花费大量时间。信息技术
return Observable.Create<DbObject>(obs =>
{
Observable.Start(() => {
var cursor = Database.Instance.GetAllObjects();
var status = cursor.MoveToFirst();
while (status == DbStatus.OK)
{
var dbObject= Db.Create(cursor);
obs.OnNext(dbObject);
status = cursor.MoveToNext();
}
obs.OnCompleted();
}, RxApp.TaskPoolScheduler);
return Disposable.Empty;
});