如何在实体框架操作期间使应用程序响应
本文关键字:应用程序 响应 操作 实体 框架 | 更新日期: 2023-09-27 18:20:29
我有一个相当简单的WPF应用程序,它使用实体框架。应用程序的主页面有一个记录列表,我在启动时从数据库中获取这些记录。
每条记录都有一张图片,因此当无线信号较差时,操作可能会有点慢。如果可能的话,我希望这(以及我的许多SQL操作)在后台执行。我有async/await设置,起初它似乎完全按照我的意愿工作,但现在我发现我的应用程序在访问数据库时变得没有响应。
最终,我想我会在一个查询中加载文本,在另一个后台操作中加载图像,并在它们进入时加载它们。这样,我可以立即获得重要的东西,图片也可以进入后台,但从目前的情况来看,如果我这样做,它看起来仍然会锁定。
除此之外,我正在尝试实现一些处理连接问题的方法(以防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解决的问题吗?
如果从UI线程调用此方法,则会重载UI线程上下文的捕获并返回到其本身。此外,您的服务不一定是"Performant",因为它必须等到UI线程空闲后才能继续。
解决方案很简单:只需在进行调用时调用传递ConfigureAwait"false"参数的方法。
.ToListAsync().ConfigureAwaiter(false);
我希望它能帮助