Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ember.js/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 缓冲来自数据库游标的数据,同时保持UI响应_C#_Wpf_System.reactive_Reactiveui - Fatal编程技术网

C# 缓冲来自数据库游标的数据,同时保持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

我有一个填充的数据库目录和一个可用于检索对象的游标。这个目录显然非常大,我想做的是使用ReactiveUI缓冲数据,同时保持UI数据绑定和响应。我按照以下步骤将我的
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;
});