Handle async exceptions when not rendering root component. Fixes #8151
This commit is contained in:
parent
7b24304739
commit
7111c8ffd7
|
|
@ -328,13 +328,14 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
HandleException(task.Exception.GetBaseException());
|
||||
break;
|
||||
default:
|
||||
// We are not in rendering the root component.
|
||||
if (_pendingTasks == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// It's important to evaluate the following even if we're not going to use
|
||||
// handledErrorTask below, because it has the side-effect of calling HandleException.
|
||||
var handledErrorTask = GetErrorHandledTask(task);
|
||||
|
||||
_pendingTasks.Add(GetErrorHandledTask(task));
|
||||
// The pendingTasks collection is only used during prerendering to track quiescence,
|
||||
// so will be null at other times.
|
||||
_pendingTasks?.Add(handledErrorTask);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2683,7 +2683,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExceptionsThrownAsynchronouslyCanBeHandled()
|
||||
public async Task ExceptionsThrownAsynchronouslyDuringFirstRenderCanBeHandled()
|
||||
{
|
||||
// Arrange
|
||||
var renderer = new TestRenderer { ShouldHandleExceptions = true };
|
||||
|
|
@ -2722,6 +2722,36 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
Assert.Same(exception, Assert.Single(renderer.HandledExceptions).GetBaseException());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExceptionsThrownAsynchronouslyAfterFirstRenderCanBeHandled()
|
||||
{
|
||||
// This differs from the "during first render" case, because some aspects of the rendering
|
||||
// code paths are special cased for the first render because of prerendering.
|
||||
|
||||
// Arrange
|
||||
var renderer = new TestRenderer { ShouldHandleExceptions = true };
|
||||
var taskToAwait = Task.CompletedTask;
|
||||
var component = new TestComponent(builder =>
|
||||
{
|
||||
builder.OpenComponent<ComponentThatAwaitsTask>(0);
|
||||
builder.AddAttribute(1, nameof(ComponentThatAwaitsTask.TaskToAwait), taskToAwait);
|
||||
builder.CloseComponent();
|
||||
});
|
||||
var componentId = renderer.AssignRootComponentId(component);
|
||||
await renderer.RenderRootComponentAsync(componentId); // Not throwing on first render
|
||||
|
||||
var asyncExceptionTcs = new TaskCompletionSource<object>();
|
||||
taskToAwait = asyncExceptionTcs.Task;
|
||||
await renderer.Invoke(component.TriggerRender);
|
||||
|
||||
// Act
|
||||
var exception = new InvalidOperationException();
|
||||
asyncExceptionTcs.SetException(exception);
|
||||
|
||||
// Assert
|
||||
Assert.Same(exception, Assert.Single(renderer.HandledExceptions).GetBaseException());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExceptionsThrownAsynchronouslyFromMultipleComponentsCanBeHandled()
|
||||
{
|
||||
|
|
@ -3696,5 +3726,15 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
OnAfterRenderAsync,
|
||||
}
|
||||
}
|
||||
|
||||
private class ComponentThatAwaitsTask : ComponentBase
|
||||
{
|
||||
[Parameter] public Task TaskToAwait { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await TaskToAwait;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue