Ensure CircuitHost disposes DI scope even if other disposals throw
This commit is contained in:
parent
002cb51d72
commit
ee1cbda155
|
|
@ -293,10 +293,16 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
|
|
||||||
await Renderer.InvokeAsync(async () =>
|
await Renderer.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
await OnConnectionDownAsync(CancellationToken.None);
|
try
|
||||||
await OnCircuitDownAsync();
|
{
|
||||||
Renderer.Dispose();
|
await OnConnectionDownAsync(CancellationToken.None);
|
||||||
_scope.Dispose();
|
await OnCircuitDownAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Renderer.Dispose();
|
||||||
|
_scope.Dispose();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -40,6 +41,37 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
Assert.True(remoteRenderer.Disposed);
|
Assert.True(remoteRenderer.Disposed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DisposeAsync_DisposesResourcesEvenIfCircuitHandlerOrComponentThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var serviceScope = new Mock<IServiceScope>();
|
||||||
|
var handler = new Mock<CircuitHandler>();
|
||||||
|
handler
|
||||||
|
.Setup(h => h.OnCircuitClosedAsync(It.IsAny<Circuit>(), It.IsAny<CancellationToken>()))
|
||||||
|
.Throws<InvalidTimeZoneException>();
|
||||||
|
var remoteRenderer = GetRemoteRenderer(Renderer.CreateDefaultDispatcher());
|
||||||
|
var circuitHost = TestCircuitHost.Create(
|
||||||
|
Guid.NewGuid().ToString(),
|
||||||
|
serviceScope.Object,
|
||||||
|
remoteRenderer,
|
||||||
|
handlers: new[] { handler.Object });
|
||||||
|
|
||||||
|
var throwOnDisposeComponent = new ThrowOnDisposeComponent();
|
||||||
|
circuitHost.Renderer.AssignRootComponentId(throwOnDisposeComponent);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await Assert.ThrowsAsync<InvalidTimeZoneException>(async () =>
|
||||||
|
{
|
||||||
|
await circuitHost.DisposeAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(throwOnDisposeComponent.DidCallDispose);
|
||||||
|
serviceScope.Verify(scope => scope.Dispose(), Times.Once());
|
||||||
|
Assert.True(remoteRenderer.Disposed);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task DisposeAsync_DisposesRendererWithinSynchronizationContext()
|
public async Task DisposeAsync_DisposesRendererWithinSynchronizationContext()
|
||||||
{
|
{
|
||||||
|
|
@ -239,5 +271,20 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
Assert.Same(Dispatcher, SynchronizationContext.Current);
|
Assert.Same(Dispatcher, SynchronizationContext.Current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ThrowOnDisposeComponent : IComponent, IDisposable
|
||||||
|
{
|
||||||
|
public bool DidCallDispose { get; private set; }
|
||||||
|
public void Configure(RenderHandle renderHandle) { }
|
||||||
|
|
||||||
|
public Task SetParametersAsync(ParameterCollection parameters)
|
||||||
|
=> Task.CompletedTask;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DidCallDispose = true;
|
||||||
|
throw new InvalidFilterCriteriaException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue