简体   繁体   English

ASP.NET 的可等待事件处理程序委托

[英]awaitable event handler delegate for ASP.NET

I have an ASP.NET MVC/WebAPI application where the domain logic depends on events in certain circumstances to decouple concerns.我有一个 ASP.NET MVC/WebAPI 应用程序,其中域逻辑在某些情况下依赖于事件来解耦关注点。 It's becoming increasingly difficult to avoid using async methods in the event handlers, but being that this is a web application I want to avoid using async void as these are not top level events that we're dealing with.在事件处理程序中避免使用异步方法变得越来越困难,但由于这是一个 web 应用程序,我想避免使用async void ,因为这些不是我们正在处理的顶级事件。 I've seen some solutions that seem overly complex for dealing with this- I'd like to keep the solution to this simple.我见过一些似乎过于复杂的解决方案来处理这个问题——我想保持这个简单的解决方案。 My solution is to ditch EventHandler delegates and use a Func that returns a Task instead, eg:我的解决方案是EventHandler委托并使用返回TaskFunc ,例如:

public event EventHandler<MyEventArgs> SomethingHappened;

would be refactored to:将被重构为:

public event Func<object, MyEventArgs, Task> SomethingHappened;

so in my code I can do this:所以在我的代码中我可以这样做:

if (SomethingHappened != null)
{
    await SomethingHappened.Invoke(this, new MyEventArgs());
}

We're the only one consuming these projects, so using the standard convention of EventHandler isn't absolutely necessary.我们是唯一使用这些项目的人,所以使用EventHandler的标准约定并不是绝对必要的。 While using this pattern implies knowledge that the handler is async, I'm not sure that's necessarily a bad thing as more and more libraries are dropping their synchronous API methods or simply not including them to begin with.虽然使用这种模式意味着知道处理程序是异步的,但我不确定这一定是一件坏事,因为越来越多的库正在放弃它们的同步 API 方法,或者根本不包括它们。 To some extent I'm surprised this isn't supported as a first class concept within .NET with async/await becoming increasingly prevalent in many libraries that are commonly used by web applications.在某种程度上,我很惊讶这不支持作为 .NET 中的第一个 class 概念,异步/等待在 web 应用程序常用的许多库中变得越来越普遍。

This seems like an elegant solution.这似乎是一个优雅的解决方案。 I've tested this in a real application with multiple subscribers to the event, with different delays for each handler, and Invoke() awaits them all.我已经在具有多个事件订阅者的真实应用程序中对此进行了测试,每个处理程序具有不同的延迟,并且Invoke()等待它们。 Yet, it feels like a trap.然而,这感觉就像一个陷阱。 What am I missing?我错过了什么?

Yet, it feels like a trap.然而,这感觉就像一个陷阱。 What am I missing?我错过了什么?

This part isn't correct:这部分不正确:

Invoke() awaits them all. Invoke() 等待他们所有人。

From the docs :文档

Invocation of a delegate instance whose invocation list contains multiple entries proceeds by invoking each of the methods in the invocation list, synchronously, in order... If the delegate invocation includes output parameters or a return value, their final value will come from the invocation of the last delegate in the list.调用列表中包含多个条目的委托实例的调用通过按顺序同步调用调用列表中的每个方法来进行...列表中的最后一个代表。

So, the Invoke will invoke all the handlers, but only return the Task from the last handler.因此, Invoke将调用所有处理程序,但仅从最后一个处理程序返回Task The other returned tasks are ignored.其他返回的任务将被忽略。 Which is Very Bad (try throwing an exception from one of the ignored tasks).这是非常糟糕的(尝试从被忽略的任务之一中抛出异常)。

Instead, you should call GetInvocationList to get the list of handlers, invoke each one, and then await them all using Task.WhenAll :相反,您应该调用GetInvocationList来获取处理程序列表,调用每个处理程序,然后使用Task.WhenAll await它们:

var args = new MyEventArgs();
var tasks = SomethingHappened.GetInvocationList()
    .Cast<Func<object, MyEventArgs, Task>>()
    .Select(handler => handler(this, args))
    .ToList();
await Task.WhenAll(tasks);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM