Description
If you subscribe to the IEventHandler<ActivityExecuting> and IEventHandler<ActivityExecuted> to track everything before and after an activity runs you can get different behaviour depending on how the fault is created.
For example setting the fault like
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
context.Fault(new Exception())
}
In this case you would get an event for ActivityExecuting and ActivityExecuted. However if you do this
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
throw new Exception();
}
You will only get ActivityExecuting because the Exception bubbles up the pipeline and skips NotificationPublishingMiddleware and gets processed by the ExceptionHandlingMiddleware.
Steps to Reproduce
Create a notification class and subscribe to both events, have one activity throw an another set the fault. Notice the difference.
internal class SuperHandler :
INotificationHandler<ActivityExecuting>,
INotificationHandler<ActivityExecuted>
{
public Task HandleAsync(ActivityExecuted notification, CancellationToken cancellationToken)
{
// This gets called only if the activity does not use a `throw` statement to throw an exception.
return Task.CompletedTask;
}
public Task HandleAsync(ActivityExecuting notification, CancellationToken cancellationToken)
{
// This gets called for every activity
return Task.CompletedTask;
}
}
Reproduction Rate: 100% of the time
Expected Behavior
This could be fixed in a few different ways
- Move the exception handler up in the pipeline, I don't think this is a good idea as it won't catch exception from other middleware.
- Handle exceptions from activities at the invocation level and use the exception handler for just exceptions at the middleware level.
- Add a new event ActivityFaulted that looks like
public record ActivityFaulted(ActivityExecutionContext ActivityExecutionContext, Exception Falut) : INotification;
you would need to pass the exception because depending on how the fault is created the context would not be in the faulted state yet.
Actual Behavior
You can't know (unless you write your own middleware) when events complete but become faulted due to an unhandled exception
Description
If you subscribe to the
IEventHandler<ActivityExecuting>andIEventHandler<ActivityExecuted>to track everything before and after an activity runs you can get different behaviour depending on how the fault is created.For example setting the fault like
In this case you would get an event for
ActivityExecutingandActivityExecuted. However if you do thisYou will only get
ActivityExecutingbecause the Exception bubbles up the pipeline and skipsNotificationPublishingMiddlewareand gets processed by theExceptionHandlingMiddleware.Steps to Reproduce
Create a notification class and subscribe to both events, have one activity throw an another set the fault. Notice the difference.
Reproduction Rate: 100% of the time
Expected Behavior
This could be fixed in a few different ways
you would need to pass the exception because depending on how the fault is created the context would not be in the faulted state yet.
Actual Behavior
You can't know (unless you write your own middleware) when events complete but become faulted due to an unhandled exception