Ensure render batches aren't started from inside each other (#12917)

This commit is contained in:
Steve Sanderson 2019-08-06 22:40:02 +01:00 committed by GitHub
parent 594916344d
commit 08349aba69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 0 deletions

View File

@ -409,6 +409,12 @@ namespace Microsoft.AspNetCore.Components.Rendering
private void ProcessRenderQueue()
{
EnsureSynchronizationContext();
if (_isBatchInProgress)
{
throw new InvalidOperationException("Cannot start a batch when one is already in progress.");
}
_isBatchInProgress = true;
var updateDisplayTask = Task.CompletedTask;

View File

@ -3395,6 +3395,27 @@ namespace Microsoft.AspNetCore.Components.Test
}
}
[Fact]
public void CannotStartOverlappingBatches()
{
// Arrange
var renderer = new InvalidRecursiveRenderer();
var component = new CallbackOnRenderComponent(() =>
{
// The renderer disallows one batch to be started inside another, because that
// would violate all kinds of state tracking invariants. It's not something that
// would ever happen except if you subclass the renderer and do something unsupported
// that commences batches from inside each other.
renderer.ProcessPendingRender();
});
var componentId = renderer.AssignRootComponentId(component);
// Act/Assert
var ex = Assert.Throws<InvalidOperationException>(
() => renderer.RenderRootComponent(componentId));
Assert.Contains("Cannot start a batch when one is already in progress.", ex.Message);
}
private class NoOpRenderer : Renderer
{
public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance)
@ -4109,5 +4130,24 @@ namespace Microsoft.AspNetCore.Components.Test
private class DerivedEventArgs : EventArgs
{
}
class CallbackOnRenderComponent : AutoRenderComponent
{
private readonly Action _callback;
public CallbackOnRenderComponent(Action callback)
{
_callback = callback;
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
=> _callback();
}
class InvalidRecursiveRenderer : TestRenderer
{
public new void ProcessPendingRender()
=> base.ProcessPendingRender();
}
}
}