C# 如何在MVVM中组合异步和非异步服务? 脚本
在基于MVVMLight的应用程序中,我使用一些视图和服务来计算工作时间。除此之外,还有以下服务C# 如何在MVVM中组合异步和非异步服务? 脚本,c#,mvvm,async-await,mvvm-light,C#,Mvvm,Async Await,Mvvm Light,在基于MVVMLight的应用程序中,我使用一些视图和服务来计算工作时间。除此之外,还有以下服务 数据服务:顾名思义,用于从数据库获取数据。因为本质上是I/O操作,所以这些方法使用的是async/await 合同:这是一个CPU绑定的服务,实际计算工作时间(例如,给定的一天、一周、一个月等) 现在,让我们来看看以下方法(位于合同服务中): 此方法位于合同服务中。它依赖于数据服务,以获取计算所需的数据 如您所见,DataService.GetWorkDay()目前是同步的。在相当长的一段时间内
- 数据服务:顾名思义,用于从数据库获取数据。因为本质上是I/O操作,所以这些方法使用的是async/await
- 合同:这是一个CPU绑定的服务,实际计算工作时间(例如,给定的一天、一周、一个月等)
合同
服务中。它依赖于数据服务
,以获取计算所需的数据
如您所见,DataService.GetWorkDay()
目前是同步的。在相当长的一段时间内,数据库就是这样被访问的。现在,在将应用程序从WP8 Silverlight移植到UWP时,我想清理代码并更改数据服务,使其只提供async
方法,而不是同步方法。但现在,我正在努力将两个实际上独立的服务组合起来,其中一个是同步的,而另一个是异步的
问题
我需要更新方法getworktimeorday()
(以及许多其他类似的方法),以使用新的DataService.getworkdaysync()
方法,该方法的名称如下所示:
public async Task<WorkDay> GetWorkDayAsync(Date dateKey)
虽然这看起来很简单,但它对代码的其余部分有很多影响。仅仅因为调用getworktimeorday
的任何地方我都必须更改代码,因为这个方法现在是async
。我必须等待它,因此可能还需要更改调用方法。。。等等但有时不需要异步调用getworktimeorday()
,因为它已经在单独的线程或后台任务中运行。更不用说目前存在的所有单元测试了。因此,在某些情况下,它不会阻塞UI
此外,CPU限制的计算突然变得异步,这不是它应该如何工作的(至少如果我正确理解Stephen Cleary的post(和其他)的话)
2.分开服务
因此,我想到的下一个解决方案是分离服务。换句话说,不要让一个服务依赖于另一个服务。这将更改getworktimeorday()
的签名,如下所示:
public TimeSpan GetWorkTimeForDay(WorkDay workDay, Date date)
{
var finalWorkTime = TimeSpan.Zero;
if (workDay != null)
finalWorkTime = GetWorkTimeForDay(workDay);
return finalWorkTime;
}
然后,调用方法将类似于以下内容:
public async void Caller(Date date)
{
...
if (ContractCalculationCacheResults.Contains(date))
return ContractCalculationCacheResults.Get(date);
var workDay = await DataService.GetWorkDay(date);
var workTime = Contract.GetWorkTimeForDay(workDay, date);
ContractCalculationCacheResults.Put(date, workTime);
...
}
与其他解决方案一样,缺点是所有调用方方法都必须更新(及其调用方,…),尽管有时这是不必要的。此外,还将添加大量用于缓存和数据库访问的冗余代码
从MVVM的角度来看,另一个缺点可能是突然间数据被提供给了合同服务。因此,我不再使用DI,这或多或少是违反规则的,有充分的理由。(这里向我提出了如何正确地将服务注入服务的问题)
最后但并非最不重要的一点是,我还必须从getworktimeorday
方法中删除缓存。只有当结果以前没有缓存时,我才会要求数据库获取工作日,然后再进行计算
3.
实际问题
在基于MVVM的项目中,将async
服务与非async
服务结合在一起的好设计是什么
我希望保持实际计算同步,因为它们是CPU限制的。我希望保持数据访问异步,因为它们不受CPU限制,但可能需要一些时间
因此,这篇文章不是关于如何等待wait
/Task/…结果的技术解决方案,而是关于MVVM、注入到其他服务中的服务、同步和异步服务组合的架构问题
此外,CPU限制的计算突然变得异步,这不是它应该如何工作
这背后有一个简单的原因:当人们看到一个异步方法时,他们通常认为该方法将异步执行,因此不会阻塞当前线程
您的方法如下所示:
public async Task<TimeSpan> GetWorkTimeForDay(Date date)
{
...
// Get the work day from the database in a synchronous fashion
var workDay = await DataService.GetWorkDayAsync(date);
...
}
//Start... (On current thread)
//Access the database (On IO Thread, freeing up the current thread)
//Return to current thread
//Do the calculations (On the current thread)
人们可能不期望的是调用线程上的工作负载
我会选择@kevin gosse:只需将其更改为异步方法,因为它仍然有一个IO绑定部分。如果您希望其他人调用该方法,请确保该文档也有一个CPU密集部分
斯蒂芬·克利里有一些很好的例子
您的第二种解决方案方法也是完全有效的。如果您认为您的“契约”纯粹是一个计算引擎,那绝对不应该关心它的数据来自哪里。您仍然有一个解耦的、可测试的类。对我来说,它可能是一个重复的问题,而不是如何等待一个解决方案的答案
async
方法来完成。此外,突然间CPU绑定的计算变得异步,这不是它应该如何工作的(至少如果我理解这篇文章(和其他文章)斯蒂芬·克利里(Stephen Cleary)的观点正确。
这只是一个一般性建议。如果您需要异步的方法,则不管它是IO还是CPU绑定。对于UI应用程序,您确实不应该进行任何冗长的计算(无论是IO还是CPU绑定)在UI线程上。对doPlus来说,使用异步方法是正确的,建议“不要仅仅因为CPU限制的操作而使方法异步”。这是因为计算将完全占用线程,因此使用当前线程比卸载到另一个线程更有效(假设这不是UI线程)。但是在您的示例中,您的方法是异步的,因为G
//Start... (On current thread)
//Access the database (On IO Thread, freeing up the current thread)
//Return to current thread
//Do the calculations (On the current thread)