我有一个使用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来解决的吗?
答案 0 :(得分:0)
如果从UI线程调用此方法,则会重载UI线程上下文的捕获并重新自身。此外,您的服务不一定是“Performant”,因为它必须等到UI线程空闲才能继续。
解决方案很简单:只需在调用时调用传递ConfigureAwait“false”参数的方法。
.ToListAsync().ConfigureAwaiter(false);
我希望它有所帮助