ASP.NET的可等待事件处理程序委托
c#
我有一个 ASP.NET MVC/WebAPI 应用程序,其中域逻辑在某些情况下依赖于事件来解耦问题。在事件处理程序中避免使用异步方法变得越来越困难,但这是一个我想避免使用的 Web 应用程序,async void因为这些不是我们正在处理的顶级事件。我已经看到一些解决方案对于处理这个问题似乎过于复杂 - 我想保持这个简单的解决方案。我的解决方案是放弃EventHandler委托并使用Func返回 a 的 aTask代替,例如:
public event EventHandler<MyEventArgs> SomethingHappened;
将被重构为:
public event Func<object, MyEventArgs, Task> SomethingHappened;
所以在我的代码中我可以这样做:
if (SomethingHappened != null)
{
await SomethingHappened.Invoke(this, new MyEventArgs());
}
我们是唯一使用这些项目的人,因此使用 的标准约定EventHandler并不是绝对必要的。虽然使用这种模式意味着知道处理程序是异步的,但我不确定这是否一定是件坏事,因为越来越多的库正在放弃它们的同步 API 方法或根本不包括它们。在某种程度上,我很惊讶这在 .NET 中不被支持作为一流的概念,因为 async/await 在许多 Web 应用程序常用的库中变得越来越普遍。
这似乎是一个优雅的解决方案。我已经在具有多个事件订阅者的真实应用程序中对此进行了测试,每个处理程序具有不同的延迟,并Invoke()等待所有事件。然而,这感觉就像一个陷阱。我错过了什么?
回答
然而,这感觉就像一个陷阱。我错过了什么?
这部分不正确:
Invoke() 等待他们所有人。
从文档:
调用一个委托实例,其调用列表包含多个条目,通过调用调用列表中的每个方法,同步,按顺序继续......如果委托调用包含输出参数或返回值,它们的最终值将来自调用列表中的最后一位代表。
因此,Invoke会调用所有的处理程序,但只返回Task从最后的处理程序。其他返回的任务将被忽略。这是非常糟糕的(尝试从被忽略的任务之一抛出异常)。
相反,您应该调用GetInvocationList以获取处理程序列表,调用每个处理程序,然后await使用它们全部调用Task.WhenAll:
var args = new MyEventArgs();
var tasks = SomethingHappened.GetInvocationList()
.Cast<Func<object, MyEventArgs, Task>>()
.Select(handler => handler(this, args))
.ToList();
await Task.WhenAll(tasks);