@@ -22,7 +22,7 @@ internal partial class CircuitHost : IAsyncDisposable
2222 private readonly CircuitOptions _options ;
2323 private readonly RemoteNavigationManager _navigationManager ;
2424 private readonly ILogger _logger ;
25- private readonly Func < Func < Task > , Task > _dispatchInboundActivity ;
25+ private Func < Func < Task > , Task > _dispatchInboundActivity ;
2626 private CircuitHandler [ ] _circuitHandlers ;
2727 private bool _initialized ;
2828 private bool _isFirstUpdate = true ;
@@ -735,11 +735,10 @@ internal Task UpdateRootComponents(
735735
736736 return Renderer . Dispatcher . InvokeAsync ( async ( ) =>
737737 {
738- var webRootComponentManager = Renderer . GetOrCreateWebRootComponentManager ( ) ;
739738 var shouldClearStore = false ;
739+ var shouldWaitForQuiescence = false ;
740740 var operations = operationBatch . Operations ;
741741 var batchId = operationBatch . BatchId ;
742- Task [ ] ? pendingTasks = null ;
743742 try
744743 {
745744 if ( Descriptors . Count > 0 )
@@ -752,6 +751,7 @@ internal Task UpdateRootComponents(
752751 if ( _isFirstUpdate )
753752 {
754753 _isFirstUpdate = false ;
754+ shouldWaitForQuiescence = true ;
755755 if ( store != null )
756756 {
757757 shouldClearStore = true ;
@@ -763,6 +763,7 @@ internal Task UpdateRootComponents(
763763
764764 // Retrieve the circuit handlers at this point.
765765 _circuitHandlers = [ .. _scope . ServiceProvider . GetServices < CircuitHandler > ( ) . OrderBy ( h => h . Order ) ] ;
766+ _dispatchInboundActivity = BuildInboundActivityDispatcher ( _circuitHandlers , Circuit ) ;
766767 await OnCircuitOpenedAsync ( cancellation ) ;
767768 await OnConnectionUpAsync ( cancellation ) ;
768769
@@ -774,44 +775,9 @@ internal Task UpdateRootComponents(
774775 throw new InvalidOperationException ( $ "The first set of update operations must always be of type { nameof ( RootComponentOperationType . Add ) } ") ;
775776 }
776777 }
777-
778- pendingTasks = new Task [ operations . Length ] ;
779- }
780-
781- for ( var i = 0 ; i < operations . Length ; i ++ )
782- {
783- var operation = operations [ i ] ;
784- switch ( operation . Type )
785- {
786- case RootComponentOperationType . Add :
787- var task = webRootComponentManager . AddRootComponentAsync (
788- operation . SsrComponentId ,
789- operation . Descriptor . ComponentType ,
790- operation . Marker . Value . Key ,
791- operation . Descriptor . Parameters ) ;
792- if ( pendingTasks != null )
793- {
794- pendingTasks [ i ] = task ;
795- }
796- break ;
797- case RootComponentOperationType . Update :
798- // We don't need to await component updates as any unhandled exception will be reported and terminate the circuit.
799- _ = webRootComponentManager . UpdateRootComponentAsync (
800- operation . SsrComponentId ,
801- operation . Descriptor . ComponentType ,
802- operation . Marker . Value . Key ,
803- operation . Descriptor . Parameters ) ;
804- break ;
805- case RootComponentOperationType . Remove :
806- webRootComponentManager . RemoveRootComponent ( operation . SsrComponentId ) ;
807- break ;
808- }
809778 }
810779
811- if ( pendingTasks != null )
812- {
813- await Task . WhenAll ( pendingTasks ) ;
814- }
780+ await PerformRootComponentOperations ( operations , shouldWaitForQuiescence ) ;
815781
816782 await Client . SendAsync ( "JS.EndUpdateRootComponents" , batchId ) ;
817783
@@ -837,6 +803,58 @@ internal Task UpdateRootComponents(
837803 } ) ;
838804 }
839805
806+ private async ValueTask PerformRootComponentOperations (
807+ RootComponentOperation [ ] operations ,
808+ bool shouldWaitForQuiescence )
809+ {
810+ var webRootComponentManager = Renderer . GetOrCreateWebRootComponentManager ( ) ;
811+ var pendingTasks = shouldWaitForQuiescence
812+ ? new Task [ operations . Length ]
813+ : null ;
814+
815+ // The inbound activity pipeline needs to be awaited because it populates
816+ // the pending tasks used to wait for quiescence.
817+ await HandleInboundActivityAsync ( ( ) =>
818+ {
819+ for ( var i = 0 ; i < operations . Length ; i ++ )
820+ {
821+ var operation = operations [ i ] ;
822+ switch ( operation . Type )
823+ {
824+ case RootComponentOperationType . Add :
825+ var task = webRootComponentManager . AddRootComponentAsync (
826+ operation . SsrComponentId ,
827+ operation . Descriptor . ComponentType ,
828+ operation . Marker . Value . Key ,
829+ operation . Descriptor . Parameters ) ;
830+ if ( pendingTasks != null )
831+ {
832+ pendingTasks [ i ] = task ;
833+ }
834+ break ;
835+ case RootComponentOperationType . Update :
836+ // We don't need to await component updates as any unhandled exception will be reported and terminate the circuit.
837+ _ = webRootComponentManager . UpdateRootComponentAsync (
838+ operation . SsrComponentId ,
839+ operation . Descriptor . ComponentType ,
840+ operation . Marker . Value . Key ,
841+ operation . Descriptor . Parameters ) ;
842+ break ;
843+ case RootComponentOperationType . Remove :
844+ webRootComponentManager . RemoveRootComponent ( operation . SsrComponentId ) ;
845+ break ;
846+ }
847+ }
848+
849+ return Task . CompletedTask ;
850+ } ) ;
851+
852+ if ( pendingTasks != null )
853+ {
854+ await Task . WhenAll ( pendingTasks ) ;
855+ }
856+ }
857+
840858 private static partial class Log
841859 {
842860 // 100s used for lifecycle stuff
0 commit comments