如何在实体框架操作期间使我的应用程序响应?

时间:2015-11-03 21:48:09

标签: c# .net wpf entity-framework asynchronous

我有一个使用Entity Framework的相当简单的WPF应用程序。应用程序的主页面包含我在启动时从数据库获取的记录列表。

每个记录都有一张图片,因此当无线信号较差时,操作可能会有点慢。如果可能的话,我喜欢这个(以及我的许多SQL操作)在后台执行。我有异步/等待设置,起初它似乎完全按照我的意愿工作,但现在我看到我的应用程序在访问数据库时变得没有响应。

最终,我想我会在一个查询中加载文本,在另一个后台操作中加载图片,并在它们进来时加载它们。这样我就可以立即获得重要的东西,并且图片可以在后台进入,但事情发展的方式仍然看起来如果我这样做会锁定。

最重要的是,我尝试实施某些功能来处理连接问题(如果wifi暂时中断),以便应用程序通知用户连接问题,自动重试几次,等等。我为SQL异常设置了一个try catch,这似乎对我有用,但整个应用程序在尝试连接到数据库时会锁定大约一分钟。

我尝试使用await Task.Delay()测试我的async / await,并且等待延迟时所有内容都按预期响应,但等待.ToListAsync()时所有内容都会锁定。这是正常的和预期的吗?我对async / await的理解非常有限。

我的代码有点混乱(我是新的),但它完成了我在大多数情况下需要它做的事情。我知道我可以做出很多改进,并且可以采取更好的方法,但这里一步一步。我现在的主要目标是在数据库访问异常期间防止应用程序崩溃,并让用户通知应用程序正在做什么(搜索,尝试访问数据库,无法访问数据库并重试等)而不是冻结这是他们在超过一分钟内没有反应时会想到的。

我的一些代码:

在我的主视图模型中

DataHelper data = new DataHelper();
private async void GetQualityRegisterQueueAsync()
    {
        try
        {
            var task = data.GetQualityRegisterAsync();
            IsSearching = true;
            await task;
            IsSearching = false;
            QualityRegisterItems = new ObservableCollection<QualityRegisterQueue>(task.Result);
            OrderQualityRegisterItems();
        }
        catch (M1Exception ex)
        {

            Debug.WriteLine(ex.Message);
            Debug.WriteLine("QualityRegisterLogViewModel.GetQualityRegisterQueue() Operation Failed");
        }

    }

我的数据助手类

public class DataHelper
{

    private bool debugging = false;

    private const int MAX_RETRY = 2;
    private const double LONG_WAIT_SECONDS = 5;
    private const double SHORT_WAIT_SECONDS = 0.5;
    private static readonly TimeSpan longWait = TimeSpan.FromSeconds(LONG_WAIT_SECONDS);
    private static readonly TimeSpan shortWait = TimeSpan.FromSeconds(SHORT_WAIT_SECONDS);
    private enum RetryableSqlErrors
    {
        ServerNotFound = -1,
        Timeout = -2,
        NoLock = 1204,
        Deadlock = 1205,
    }

    public async   Task<List<QualityRegisterQueue>> GetQualityRegisterAsync()
    {
       if(debugging) await Task.Delay(5000);

       var retryCount = 0;
       using (M1Context m1 = new M1Context())
       {
           for (; ; )
           {
               try
               {
                   return await (from a in m1.QualityRegisters
                                 where (a.qanClosed == 0)
                                 //orderby a.qanAssignedDate descending, a.qanOpenedDate
                                 orderby a.qanAssignedDate.HasValue descending, a.qanAssignedDate, a.qanOpenedDate
                                 select new QualityRegisterQueue
                                 {
                                     QualityRegisterID = a.qanQualityRegisterID,
                                     JobID = a.qanJobID.Trim(),
                                     JobAssemblyID = a.qanJobAssemblyID,
                                     JobOperationID = a.qanJobOperationID,
                                     PartID = a.qanPartID.Trim(),
                                     PartRevisionID = a.qanPartRevisionID.Trim(),
                                     PartShortDescription = a.qanPartShortDescription.Trim(),
                                     OpenedByEmployeeID = a.qanOpenedByEmployeeID.Trim(),
                                     OpenedByEmployeeName = a.OpenedEmployee.lmeEmployeeName.Trim(),
                                     OpenedDate = a.qanOpenedDate,
                                     PartImage = a.JobAssembly.ujmaPartImage,
                                     AssignedDate = a.qanAssignedDate,
                                     AssignedToEmployeeID = a.qanAssignedToEmployeeID.Trim(),
                                     AssignedToEmployeeName = a.AssignedEmployee.lmeEmployeeName.Trim()
                                 }).ToListAsync();
               }
               catch (SqlException ex)
               {
                   Debug.WriteLine("SQL Exception number = " + ex.Number);
                   if (!Enum.IsDefined(typeof(RetryableSqlErrors), ex.Number))
                       throw new M1Exception(ex.Message, ex);

                   retryCount++;

                   if (retryCount > MAX_RETRY) throw new M1Exception(ex.Message, ex); ;
                    Debug.WriteLine("Retrying. Count = " + retryCount);
                   Thread.Sleep(ex.Number == (int)RetryableSqlErrors.Timeout ?
                                                           longWait : shortWait);
               }
           }
       }


    }


}

编辑:主要是在这里寻找一般性指导,尽管如何做的具体例子会很棒。对于我正在下载数据的这些类型的操作,如果我需要应用程序响应,我需要制作多个线程吗?这是解决这类问题的常见方法吗?这不是我应该期待async / await来解决的吗?

1 个答案:

答案 0 :(得分:0)

如果从UI线程调用此方法,则会重载UI线程上下文的捕获并重新自身。此外,您的服务不一定是“Performant”,因为它必须等到UI线程空闲才能继续。

解决方案很简单:只需在调用时调用传递ConfigureAwait“false”参数的方法。

.ToListAsync().ConfigureAwaiter(false);

我希望它有所帮助