Skip to content

Commit 85d5d6b

Browse files
[release/8.0-preview3] Follow up 'Ability to monitor Blazor Server circuit activity' (#47354)
* Follow up 'Ability to monitor Blazor Server circuit activity' (#47328) * PR feedback
1 parent b1ed9b9 commit 85d5d6b

File tree

6 files changed

+67
-50
lines changed

6 files changed

+67
-50
lines changed

src/Components/Server/src/Circuits/CircuitHandler.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,11 @@ public abstract class CircuitHandler
7272
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
7373
/// <returns><see cref="Task"/> that represents the asynchronous execution operation.</returns>
7474
public virtual Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken) => Task.CompletedTask;
75+
76+
/// <summary>
77+
/// Creates a handler that gets invoked when inbound activity on the circuit causes an asynchronous task to be dispatched on the server.
78+
/// </summary>
79+
/// <param name="next">The next handler to invoke.</param>
80+
/// <returns>A handler function that returns a <see cref="Task"/> that completes when the activity has finished.</returns>
81+
public virtual Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(Func<CircuitInboundActivityContext, Task> next) => next;
7582
}

src/Components/Server/src/Circuits/CircuitHost.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -606,24 +606,22 @@ internal async Task<TResult> HandleInboundActivityAsync<TResult>(Func<Task<TResu
606606

607607
private static Func<Func<Task>, Task> BuildInboundActivityDispatcher(IReadOnlyList<CircuitHandler> circuitHandlers, Circuit circuit)
608608
{
609-
Func<CircuitInboundActivityContext, Task>? result = null;
609+
var inner = static (CircuitInboundActivityContext context) => context.Handler();
610+
var result = inner;
610611

611612
for (var i = circuitHandlers.Count - 1; i >= 0; i--)
612613
{
613-
if (circuitHandlers[i] is IHandleCircuitActivity inboundActivityHandler)
614-
{
615-
var next = result ?? (static (context) => context.Handler());
616-
result = (context) => inboundActivityHandler.HandleInboundActivityAsync(context, next);
617-
}
614+
var next = result;
615+
result = circuitHandlers[i].CreateInboundActivityHandler(next);
618616
}
619617

620-
if (result is null)
618+
if (result == inner)
621619
{
622620
// If there are no registered handlers, there is no need to allocate a context on each call.
623-
return static (handler) => handler();
621+
return static handler => handler();
624622
}
625623

626-
return (handler) => result(new(handler, circuit));
624+
return handler => result(new(handler, circuit));
627625
}
628626

629627
private void AssertInitialized()

src/Components/Server/src/Circuits/IHandleCircuitActivity.cs

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#nullable enable
22
Microsoft.AspNetCore.Components.Server.Circuits.CircuitInboundActivityContext
33
Microsoft.AspNetCore.Components.Server.Circuits.CircuitInboundActivityContext.Circuit.get -> Microsoft.AspNetCore.Components.Server.Circuits.Circuit!
4-
Microsoft.AspNetCore.Components.Server.Circuits.IHandleCircuitActivity
5-
Microsoft.AspNetCore.Components.Server.Circuits.IHandleCircuitActivity.HandleInboundActivityAsync(Microsoft.AspNetCore.Components.Server.Circuits.CircuitInboundActivityContext! context, System.Func<Microsoft.AspNetCore.Components.Server.Circuits.CircuitInboundActivityContext!, System.Threading.Tasks.Task!>! next) -> System.Threading.Tasks.Task!
4+
virtual Microsoft.AspNetCore.Components.Server.Circuits.CircuitHandler.CreateInboundActivityHandler(System.Func<Microsoft.AspNetCore.Components.Server.Circuits.CircuitInboundActivityContext!, System.Threading.Tasks.Task!>! next) -> System.Func<Microsoft.AspNetCore.Components.Server.Circuits.CircuitInboundActivityContext!, System.Threading.Tasks.Task!>!

src/Components/Server/test/Circuits/CircuitHostTest.cs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ public async Task InitializeAsync_InvokesHandlers()
157157
var handler2 = new Mock<CircuitHandler>(MockBehavior.Strict);
158158
var sequence = new MockSequence();
159159

160+
SetupMockInboundActivityHandlers(sequence, handler1, handler2);
161+
160162
handler1
161163
.InSequence(sequence)
162164
.Setup(h => h.OnCircuitOpenedAsync(It.IsAny<Circuit>(), cancellationToken))
@@ -242,6 +244,8 @@ public async Task InitializeAsync_ReportsOwnAsyncExceptions()
242244
var tcs = new TaskCompletionSource();
243245
var reportedErrors = new List<UnhandledExceptionEventArgs>();
244246

247+
SetupMockInboundActivityHandler(handler);
248+
245249
handler
246250
.Setup(h => h.OnCircuitOpenedAsync(It.IsAny<Circuit>(), It.IsAny<CancellationToken>()))
247251
.Returns(tcs.Task)
@@ -284,6 +288,8 @@ public async Task DisposeAsync_InvokesCircuitHandler()
284288
var handler2 = new Mock<CircuitHandler>(MockBehavior.Strict);
285289
var sequence = new MockSequence();
286290

291+
SetupMockInboundActivityHandlers(sequence, handler1, handler2);
292+
287293
handler1
288294
.InSequence(sequence)
289295
.Setup(h => h.OnConnectionDownAsync(It.IsAny<Circuit>(), cancellationToken))
@@ -327,29 +333,31 @@ public async Task HandleInboundActivityAsync_InvokesCircuitActivityHandlers()
327333
var handler3 = new Mock<CircuitHandler>(MockBehavior.Strict);
328334
var sequence = new MockSequence();
329335

330-
// We deliberately avoid making handler2 an inbound activity handler
331-
var activityHandler1 = handler1.As<IHandleCircuitActivity>();
332-
var activityHandler3 = handler3.As<IHandleCircuitActivity>();
333-
334336
var asyncLocal1 = new AsyncLocal<bool>();
335337
var asyncLocal3 = new AsyncLocal<bool>();
336338

337-
activityHandler1
339+
handler3
338340
.InSequence(sequence)
339-
.Setup(h => h.HandleInboundActivityAsync(It.IsAny<CircuitInboundActivityContext>(), It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
340-
.Returns(async (CircuitInboundActivityContext context, Func<CircuitInboundActivityContext, Task> next) =>
341+
.Setup(h => h.CreateInboundActivityHandler(It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
342+
.Returns((Func<CircuitInboundActivityContext, Task> next) => async (CircuitInboundActivityContext context) =>
341343
{
342-
asyncLocal1.Value = true;
344+
asyncLocal3.Value = true;
343345
await next(context);
344346
})
345347
.Verifiable();
346348

347-
activityHandler3
349+
handler2
350+
.InSequence(sequence)
351+
.Setup(h => h.CreateInboundActivityHandler(It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
352+
.Returns((Func<CircuitInboundActivityContext, Task> next) => next)
353+
.Verifiable();
354+
355+
handler1
348356
.InSequence(sequence)
349-
.Setup(h => h.HandleInboundActivityAsync(It.IsAny<CircuitInboundActivityContext>(), It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
350-
.Returns(async (CircuitInboundActivityContext context, Func<CircuitInboundActivityContext, Task> next) =>
357+
.Setup(h => h.CreateInboundActivityHandler(It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
358+
.Returns((Func<CircuitInboundActivityContext, Task> next) => async (CircuitInboundActivityContext context) =>
351359
{
352-
asyncLocal3.Value = true;
360+
asyncLocal1.Value = true;
353361
await next(context);
354362
})
355363
.Verifiable();
@@ -367,8 +375,9 @@ await circuitHost.HandleInboundActivityAsync(() =>
367375
});
368376

369377
// Assert
370-
activityHandler1.VerifyAll();
371-
activityHandler3.VerifyAll();
378+
handler1.VerifyAll();
379+
handler2.VerifyAll();
380+
handler3.VerifyAll();
372381

373382
Assert.False(asyncLocal1.Value);
374383
Assert.False(asyncLocal3.Value);
@@ -404,6 +413,26 @@ private static TestRemoteRenderer GetRemoteRenderer()
404413
Mock.Of<IClientProxy>());
405414
}
406415

416+
private static void SetupMockInboundActivityHandlers(MockSequence sequence, params Mock<CircuitHandler>[] circuitHandlers)
417+
{
418+
for (var i = circuitHandlers.Length - 1; i >= 0; i--)
419+
{
420+
circuitHandlers[i]
421+
.InSequence(sequence)
422+
.Setup(h => h.CreateInboundActivityHandler(It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
423+
.Returns((Func<CircuitInboundActivityContext, Task> next) => next)
424+
.Verifiable();
425+
}
426+
}
427+
428+
private static void SetupMockInboundActivityHandler(Mock<CircuitHandler> circuitHandler)
429+
{
430+
circuitHandler
431+
.Setup(h => h.CreateInboundActivityHandler(It.IsAny<Func<CircuitInboundActivityContext, Task>>()))
432+
.Returns((Func<CircuitInboundActivityContext, Task> next) => next)
433+
.Verifiable();
434+
}
435+
407436
private class TestRemoteRenderer : RemoteRenderer
408437
{
409438
public TestRemoteRenderer(IServiceProvider serviceProvider, IClientProxy client)

src/Components/test/testassets/TestServer/TestCircuitContextAccessor.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55

66
namespace TestServer;
77

8-
public class TestCircuitContextAccessor : CircuitHandler, IHandleCircuitActivity
8+
public class TestCircuitContextAccessor : CircuitHandler
99
{
1010
private readonly AsyncLocal<bool> _hasCircuitContext = new();
1111

1212
public bool HasCircuitContext => _hasCircuitContext.Value;
1313

14-
public async Task HandleInboundActivityAsync(CircuitInboundActivityContext context, Func<CircuitInboundActivityContext, Task> next)
15-
{
16-
_hasCircuitContext.Value = true;
17-
await next(context);
18-
_hasCircuitContext.Value = false;
19-
}
14+
public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(
15+
Func<CircuitInboundActivityContext, Task> next)
16+
=> async (context) =>
17+
{
18+
_hasCircuitContext.Value = true;
19+
await next(context);
20+
_hasCircuitContext.Value = false;
21+
};
2022
}

0 commit comments

Comments
 (0)