@@ -24,22 +24,27 @@ internal class RazorComponentEndpointDataSource<[DynamicallyAccessedMembers(Comp
2424 private readonly IApplicationBuilder _applicationBuilder ;
2525 private readonly RenderModeEndpointProvider [ ] _renderModeEndpointProviders ;
2626 private readonly RazorComponentEndpointFactory _factory ;
27-
27+ private readonly HotReloadService _hotReloadService ;
2828 private List < Endpoint > ? _endpoints ;
29- // TODO: Implement endpoint data source updates https://github.com/dotnet/aspnetcore/issues/47026
30- private readonly CancellationTokenSource _cancellationTokenSource ;
31- private readonly IChangeToken _changeToken ;
29+ private CancellationTokenSource _cancellationTokenSource ;
30+ private IChangeToken _changeToken ;
31+
32+ // Internal for testing.
33+ internal ComponentApplicationBuilder Builder => _builder ;
34+ internal List < Action < EndpointBuilder > > Conventions => _conventions ;
3235
3336 public RazorComponentEndpointDataSource (
3437 ComponentApplicationBuilder builder ,
3538 IEnumerable < RenderModeEndpointProvider > renderModeEndpointProviders ,
3639 IApplicationBuilder applicationBuilder ,
37- RazorComponentEndpointFactory factory )
40+ RazorComponentEndpointFactory factory ,
41+ HotReloadService hotReloadService )
3842 {
3943 _builder = builder ;
4044 _applicationBuilder = applicationBuilder ;
4145 _renderModeEndpointProviders = renderModeEndpointProviders . ToArray ( ) ;
4246 _factory = factory ;
47+ _hotReloadService = hotReloadService ;
4348 DefaultBuilder = new RazorComponentsEndpointConventionBuilder (
4449 _lock ,
4550 builder ,
@@ -62,7 +67,7 @@ public override IReadOnlyList<Endpoint> Endpoints
6267 // The order is as follows:
6368 // * MapRazorComponents gets called and the data source gets created.
6469 // * The RazorComponentEndpointConventionBuilder is returned and the user gets a chance to call on it to add conventions.
65- // * The first request arrives and the DfaMatcherBuilder acesses the data sources to get the endpoints.
70+ // * The first request arrives and the DfaMatcherBuilder accesses the data sources to get the endpoints.
6671 // * The endpoints get created and the conventions get applied.
6772 Initialize ( ) ;
6873 Debug . Assert ( _changeToken != null ) ;
@@ -89,47 +94,61 @@ private void Initialize()
8994
9095 private void UpdateEndpoints ( )
9196 {
92- var endpoints = new List < Endpoint > ( ) ;
93- var context = _builder . Build ( ) ;
94-
95- foreach ( var definition in context . Pages )
97+ lock ( _lock )
9698 {
97- _factory . AddEndpoints ( endpoints , typeof ( TRootComponent ) , definition , _conventions , _finallyConventions ) ;
98- }
99+ var endpoints = new List < Endpoint > ( ) ;
100+ var context = _builder . Build ( ) ;
99101
100- ICollection < IComponentRenderMode > renderModes = Options . ConfiguredRenderModes ;
102+ foreach ( var definition in context . Pages )
103+ {
104+ _factory . AddEndpoints ( endpoints , typeof ( TRootComponent ) , definition , _conventions , _finallyConventions ) ;
105+ }
101106
102- foreach ( var renderMode in renderModes )
103- {
104- var found = false ;
105- foreach ( var provider in _renderModeEndpointProviders )
107+ ICollection < IComponentRenderMode > renderModes = Options . ConfiguredRenderModes ;
108+
109+ foreach ( var renderMode in renderModes )
106110 {
107- if ( provider . Supports ( renderMode ) )
111+ var found = false ;
112+ foreach ( var provider in _renderModeEndpointProviders )
113+ {
114+ if ( provider . Supports ( renderMode ) )
115+ {
116+ found = true ;
117+ RenderModeEndpointProvider . AddEndpoints (
118+ endpoints ,
119+ typeof ( TRootComponent ) ,
120+ provider . GetEndpointBuilders ( renderMode , _applicationBuilder . New ( ) ) ,
121+ renderMode ,
122+ _conventions ,
123+ _finallyConventions ) ;
124+ }
125+ }
126+
127+ if ( ! found )
108128 {
109- found = true ;
110- RenderModeEndpointProvider . AddEndpoints (
111- endpoints ,
112- typeof ( TRootComponent ) ,
113- provider . GetEndpointBuilders ( renderMode , _applicationBuilder . New ( ) ) ,
114- renderMode ,
115- _conventions ,
116- _finallyConventions ) ;
129+ throw new InvalidOperationException ( $ "Unable to find a provider for the render mode: { renderMode . GetType ( ) . FullName } . This generally " +
130+ $ "means that a call to 'AddWebAssemblyComponents' or 'AddServerComponents' is missing. " +
131+ $ "Alternatively call 'AddWebAssemblyRenderMode', 'AddServerRenderMode' might be missing if you have set UseDeclaredRenderModes = false.") ;
117132 }
118133 }
119134
120- if ( ! found )
135+ var oldCancellationTokenSource = _cancellationTokenSource ;
136+ _endpoints = endpoints ;
137+ _cancellationTokenSource = new CancellationTokenSource ( ) ;
138+ _changeToken = new CancellationChangeToken ( _cancellationTokenSource . Token ) ;
139+ oldCancellationTokenSource ? . Cancel ( ) ;
140+ if ( _hotReloadService . MetadataUpdateSupported )
121141 {
122- throw new InvalidOperationException ( $ "Unable to find a provider for the render mode: { renderMode . GetType ( ) . FullName } . This generally " +
123- $ "means that a call to 'AddWebAssemblyComponents' or 'AddServerComponents' is missing. " +
124- $ "Alternatively call 'AddWebAssemblyRenderMode', 'AddServerRenderMode' might be missing if you have set UseDeclaredRenderModes = false.") ;
142+ ChangeToken . OnChange ( _hotReloadService . GetChangeToken , UpdateEndpoints ) ;
125143 }
126144 }
127-
128- _endpoints = endpoints ;
129145 }
146+
130147 public override IChangeToken GetChangeToken ( )
131148 {
132- // TODO: Handle updates if necessary (for hot reload).
149+ Initialize ( ) ;
150+ Debug . Assert ( _changeToken != null ) ;
151+ Debug . Assert ( _endpoints != null ) ;
133152 return _changeToken ;
134153 }
135154}
0 commit comments