Wpf从数据库冻结UI中检索数据

时间:2016-04-15 06:42:48

标签: c# wpf async-await

每次我获得UI锁定时,我都会遇到从数据库中异步检索数据的问题。

private async void RetrieveHotlist(object sender, RoutedEventArgs e) //button click
    {
        var hotItems = new ObservableCollection<HotItem>();
        await Task.Factory.StartNew(() =>
            {

                try
                {
                    var serv = "xxx";
                    string connStr = Common.GetConStrEF(serv + "\\" + Common.DBLOGIN_INSTANCE,
                                                        Common.DBLOGIN_DBNAME, Common.DBLOGIN_USER, Common.DBLOGIN_PASSWORD);
                    var dataModel = new xxxxDataModel(connStr);

                    foreach (var category in dataModel.SpecialNumberCategory)  //retrieving database CreateObjectSet<SpecialNumberCategory>("SpecialNumberCategory"); //ObjectContext
                    {
                        var item = new HotItem() {  Name = category.Name };
                        hotItems.Add(item);
                    }

                }
                catch (Exception exception)
                {
                    var baseException = exception.GetBaseException();
                    MessageBox.Show("Error\n\n" + exception.Message + "\n\n" + baseException.Message);
                }

            });
        if (Settings != null)
        {
            Settings.Hotlist.Clear();
            foreach (var hotItem in hotItems)
            {
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => Settings.Hotlist.Add(hotItem)));
            }
        }
    }

为什么RetrieveHotlist方法会锁定我的UI?为什么等待Task.Factory.StartNew()这还不够?

感谢您的帮助:)

修改

我删除了一些代码以便更清楚。

private void RetrieveHotlist(object sender, RoutedEventArgs e) //button click
    {
        var b = new BackgroundWorker();
        b.DoWork += (o, args) =>
            {
                Thread.Sleep(2000); //**UI IS FULL RESPONSIVE FOR 2 sec.**
                var hotItems = new ObservableCollection<HotItem>();
                try
                {

                    var serv = "xxxx";
                    var dataModel = new xxxxDataModel(connStr);
                    var c = dataModel.SpecialNumberCategory; //**UI FREEZE / ENTITY FRAMEWORK**


        b.RunWorkerCompleted += (o, args) =>
            {

            };
        b.RunWorkerAsync();

    }

EDIT2: 感谢大家的帮助,实体框架引发了这个问题(我现在还不知道为什么)。

我用SqlConnection和SqlCommand替换了所有模型行。现在它很好用。

2 个答案:

答案 0 :(得分:0)

应在UI线程上调用与UI相关的代码。不要将它与操作数据的相同线程(发送,检索,更新等)混合以避免死锁。在你的情况下,它是由与数据库的交互引起的。

以下是一个例子:

 Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { 
    /* Your code here */ 
    MessageBox.Show("Error\n\n" + exception.Message + "\n\n" + baseException.Message);
}));

答案 1 :(得分:0)

您的方法RetrieveHotlist()阻止代码

下的UI原因
           foreach (var hotItem in hotItems)
            {
                Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => Settings.Hotlist.Add(hotItem)));
            }

正在主Thread上执行。如果您仔细查看问题,那么您必须逐个向list添加项目,并且每次将项目添加到Hotlist时,都必须将更改提升为UI,甚至如果它没有在每个项目上执行添加事件,则需要一些时间来迭代collection并将项目添加到另一个collection,这是您的UI线程的冻结时间。 / p>

为避免这种情况,您可以直接将hotItems分配给Hotlist(通过使用相应的Ienumerable转换)。如果你能给我Hotlist的类型,我可以给你准确的语法。或者您可以先准备兼容类型的临时集合,然后将该集合分配给Hotlist关键是尽量减少UI线程的工作。替换整个foreach,如下所示:

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => Settings.Hotlist.AddRange(hotItems)));

并在foreach中移动Thread完成的工作。现在在Action中,使用AddRange(),执行assignment或其他任何操作即可。