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());
|
HandleException(task.Exception.GetBaseException());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// We are not in rendering the root component.
|
// It's important to evaluate the following even if we're not going to use
|
||||||
if (_pendingTasks == null)
|
// handledErrorTask below, because it has the side-effect of calling HandleException.
|
||||||
{
|
var handledErrorTask = GetErrorHandledTask(task);
|
||||||
return;
|
|
||||||
}
|
// The pendingTasks collection is only used during prerendering to track quiescence,
|
||||||
|
// so will be null at other times.
|
||||||
|
_pendingTasks?.Add(handledErrorTask);
|
||||||
|
|
||||||
_pendingTasks.Add(GetErrorHandledTask(task));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2683,7 +2683,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ExceptionsThrownAsynchronouslyCanBeHandled()
|
public async Task ExceptionsThrownAsynchronouslyDuringFirstRenderCanBeHandled()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var renderer = new TestRenderer { ShouldHandleExceptions = true };
|
var renderer = new TestRenderer { ShouldHandleExceptions = true };
|
||||||
|
|
@ -2722,6 +2722,36 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
Assert.Same(exception, Assert.Single(renderer.HandledExceptions).GetBaseException());
|
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]
|
[Fact]
|
||||||
public async Task ExceptionsThrownAsynchronouslyFromMultipleComponentsCanBeHandled()
|
public async Task ExceptionsThrownAsynchronouslyFromMultipleComponentsCanBeHandled()
|
||||||
{
|
{
|
||||||
|
|
@ -3696,5 +3726,15 @@ namespace Microsoft.AspNetCore.Components.Test
|
||||||
OnAfterRenderAsync,
|
OnAfterRenderAsync,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ComponentThatAwaitsTask : ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter] public Task TaskToAwait { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
await TaskToAwait;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue