@@ -5029,21 +5029,17 @@ public async Task DisposingRenderer_UnsubsribesFromHotReloadManager()
50295029 }
50305030
50315031 [ Fact ]
5032- public async Task HotReload_ReRenderPreservesAsyncLocalValues_FailsToday ( )
5032+ public async Task HotReload_ReRenderPreservesAsyncLocalValues ( )
50335033 {
5034- // This is a regression test for the reported issue: AsyncLocal values are lost during hot reload.
5035- // The desired (correct) behavior is that AsyncLocal values flow into the hot-reload re-render.
5036- // Currently, because the hot reload callback is raised from a context without the original ExecutionContext,
5037- // the AsyncLocal value is lost and the assertion below will FAIL (demonstrating the bug).
5038-
50395034 await using var renderer = new TestRenderer ( ) ;
5040- var hotReloadManager = new HotReloadManager { MetadataUpdateSupported = true } ;
5041- renderer . HotReloadManager = hotReloadManager ;
5035+
5036+ renderer . HotReloadManager = HotReloadManager . Default ;
5037+ HotReloadManager . Default . MetadataUpdateSupported = true ;
50425038
50435039 var component = new AsyncLocalCaptureComponent ( ) ;
50445040
50455041 // Establish AsyncLocal value before registering hot reload handler / rendering.
5046- AsyncLocalCaptureComponent . TestAsyncLocal . Value = "AmbientValue" ;
5042+ ServiceAccessor . TestAsyncLocal . Value = "AmbientValue" ;
50475043
50485044 var componentId = renderer . AssignRootComponentId ( component ) ;
50495045 await renderer . Dispatcher . InvokeAsync ( ( ) => renderer . RenderRootComponentAsync ( componentId ) ) ;
@@ -5052,20 +5048,16 @@ public async Task HotReload_ReRenderPreservesAsyncLocalValues_FailsToday()
50525048 Assert . Null ( component . HotReloadValue ) ;
50535049
50545050 // Simulate hot reload delta applied from a fresh thread (different ExecutionContext) so the AsyncLocal value is lost.
5055- var expected = AsyncLocalCaptureComponent . TestAsyncLocal . Value ;
5051+ var expected = ServiceAccessor . TestAsyncLocal . Value ;
50565052 var thread = new Thread ( ( ) =>
50575053 {
50585054 // Simulate environment where the ambient value is not present on the hot reload thread.
5059- AsyncLocalCaptureComponent . TestAsyncLocal . Value = null ;
5060- var evtField = typeof ( HotReloadManager ) . GetField ( "OnDeltaApplied" , BindingFlags . Instance | BindingFlags . NonPublic ) ;
5061- var del = ( Action ) evtField . GetValue ( hotReloadManager ) ;
5062- del ? . Invoke ( ) ;
5055+ ServiceAccessor . TestAsyncLocal . Value = null ;
5056+ HotReloadManager . UpdateApplication ( [ ] ) ;
50635057 } ) ;
50645058 thread . Start ( ) ;
50655059 thread . Join ( ) ;
50665060
5067- // EXPECTED (desired) correct behavior: value should still be present.
5068- // ACTUAL today: this will be null, so the test fails, confirming the bug.
50695061 Assert . Equal ( expected , component . HotReloadValue ) ;
50705062 }
50715063
@@ -5222,10 +5214,13 @@ protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
52225214 => Task . CompletedTask ;
52235215 }
52245216
5225- private class AsyncLocalCaptureComponent : IComponent
5217+ private class ServiceAccessor
52265218 {
5227- public static readonly AsyncLocal < string > TestAsyncLocal = new ( ) ;
5219+ public static AsyncLocal < string > TestAsyncLocal = new AsyncLocal < string > ( ) ;
5220+ }
52285221
5222+ private class AsyncLocalCaptureComponent : IComponent
5223+ {
52295224 private bool _initialized ;
52305225 private RenderHandle _renderHandle ;
52315226 public string HotReloadValue { get ; private set ; }
@@ -5241,7 +5236,7 @@ public Task SetParametersAsync(ParameterView parameters)
52415236 else
52425237 {
52435238 // Hot reload re-render path.
5244- HotReloadValue = TestAsyncLocal . Value ;
5239+ HotReloadValue = ServiceAccessor . TestAsyncLocal . Value ;
52455240 }
52465241 return Task . CompletedTask ;
52475242 }
0 commit comments