我很难找到一个简单灵活的模式,允许我在我的ViewModel中编写代码,这些代码在运行时可以异步运行,但在测试时同步运行。这就是我想出的 - 有没有人有任何建议?这是一条走下去的好路吗?那里有更好的现有模式吗?
LongRunningCall定义:
public class LongRunningCall
{
public Action ExecuteAction { get; set; }
public Action PostExecuteAction { get; set; }
public LongRunningCall(Action executeAction = null, Action postExecuteAction = null)
{
ExecuteAction = executeAction;
PostExecuteAction = postExecuteAction;
}
public void Execute(Action<Exception> onError)
{
try
{
ExecuteAction();
PostExecuteAction();
}
catch (Exception ex)
{
if (onError == null)
throw;
onError(ex);
}
}
public void ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
{
var executeTask = Task.Factory.StartNew(ExecuteAction);
var postExecuteTask = executeTask.ContinueWith((t) =>
{
if (t.Exception != null)
throw t.Exception;
PostExecuteAction();
}, scheduler);
if (onError != null)
postExecuteTask.ContinueWith((t) => { onError(t.Exception); });
}
}
用法:
var continueCall = new LongRunningCall(continueCommand_Execute, continueCommand_PostExecute);
if (svc.IsAsyncRequired)
continueCall.ExecuteAsync(TaskScheduler.FromCurrentSynchronizationContext(), continueCommand_Error);
else
continueCall.Execute(continueCommand_Error);
唯一真正的先决条件是你需要在运行时知道你是否应该使用异步/同步。当我运行单元测试时,我发送一个模拟器,告诉我的代码同步运行,当应用程序实际运行时IsAsyncRequired默认为true;
反馈
答案 0 :(得分:2)
我更愿意在一个单独的类中同步或异步执行代码的决定,这个类可以在这样的接口后面抽象:
public interface ITaskExecuter
{
void ScheduleTask(
Action executeAction,
Action postExecuteAction,
Action<Exception> onException);
}
可以在需要时注入实现ITaskExecuter
的类的实例。
您可以为测试与生产方案注入不同的实例。
用法变为:
taskExecuter.ScheduleTask(
continueCommand_Execute,
continueCommand_PostExecute,
continueCommand_Error);
在调用类中没有用于测试与生产的单独代码路径。
您可以选择编写以下测试:
答案 1 :(得分:2)
我在当前的工作中做了一些非常简单的事情,但是现在无法复制/粘贴代码......
基本上我所做的是使用IWorker
方法创建DoWork(Func<>)
界面。
然后我创建了2个派生类,一个是“AsyncWorker”,另一个是“SyncWorker”。 SyncWorker只执行传入的Func
(同步),而'AsyncWorker'是BackgroundWorker
的包装器,它将传递的Func
发送到BackgroundWorker,以便异步处理。
然后,我将ViewModel更改为传入IWorker
。这会将依赖项解析移出ViewModel,因此您可以使用Dep。注射液。实用程序(我使用Unity和Constructor注入)。
由于我使用Unity,因此在我的单元测试配置中,我将IWorker
映射到SyncWorker
,在制作中我将IWorker
映射到AsyncWorker
。
希望这是有道理的...我知道如果我手边有代码会更容易......
答案 2 :(得分:1)
考虑更改ExecuteAsync
,以便它返回Task
:
public Task ExecuteAsync(TaskScheduler scheduler, Action<Exception> onError)
所以在生产代码中,我只是按原样调用它:
longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
但是在单元测试中,我会等待任务真正完成:
var task = longRunningCall.ExecuteAsync(
TaskScheduler.FromCurrentSynchronizationContext(),
continueCommand_Error);
task.Wait();