如何解决ASP.NET 的可等待事件处理程序委托
我有一个 ASP.NET MVC/WebAPI 应用程序,其中域逻辑在某些情况下依赖于事件来解耦问题。在事件处理程序中避免使用异步方法变得越来越困难,但由于这是一个 Web 应用程序,我想避免使用 async void
,因为这些不是我们正在处理的顶级事件。我已经看到一些解决方案对于处理这个问题似乎过于复杂 - 我想保持这个简单的解决方案。我的解决方案是放弃 EventHandler
委托并使用返回 Func
的 Task
,例如:
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() 等待它们。
来自the docs:
一个委托实例的调用,其调用列表包含多个条目,通过调用调用列表中的每个方法,同步地,按顺序......如果委托调用包含输出参数或返回值,它们的最终值将出现来自列表中最后一个委托的调用。
因此,Invoke
将调用所有处理程序,但仅从 last 处理程序返回 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);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。