// InitializeAsync is used in a fire-and-forget context, so it's responsible for its own // error handling. public Task InitializeAsync(ProtectedPrerenderComponentApplicationStore store, CancellationToken cancellationToken) { Log.InitializationStarted(_logger); return(Renderer.Dispatcher.InvokeAsync(async() => { if (_initialized) { throw new InvalidOperationException("The circuit host is already initialized."); } try { _initialized = true; // We're ready to accept incoming JSInterop calls from here on await OnCircuitOpenedAsync(cancellationToken); await OnConnectionUpAsync(cancellationToken); // Here, we add each root component but don't await the returned tasks so that the // components can be processed in parallel. var count = Descriptors.Count; var pendingRenders = new Task[count]; for (var i = 0; i < count; i++) { var(componentType, parameters, sequence) = Descriptors[i]; pendingRenders[i] = Renderer.AddComponentAsync(componentType, parameters, sequence.ToString(CultureInfo.InvariantCulture)); } // Now we wait for all components to finish rendering. await Task.WhenAll(pendingRenders); // At this point all components have successfully produced an initial render and we can clear the contents of the component // application state store. This ensures the memory that was not used during the initial render of these components gets // reclaimed since no-one else is holding on to it any longer. store.ExistingState.Clear(); Log.InitializationSucceeded(_logger); } catch (Exception ex) { // Report errors asynchronously. InitializeAsync is designed not to throw. Log.InitializationFailed(_logger, ex); UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false)); await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex), ex); } })); }
public async Task GetPersistStateAsync_CanUnprotectPersistedState() { // Arrange var expectedState = new Dictionary <string, byte[]>() { ["MyValue"] = new byte[] { 1, 2, 3, 4 } }; var persistedState = Convert.ToBase64String(_protector.Protect(JsonSerializer.SerializeToUtf8Bytes(expectedState))); var store = new ProtectedPrerenderComponentApplicationStore(persistedState, _provider); // Act var restored = await store.GetPersistedStateAsync(); // Assert Assert.Equal(expectedState, restored); }
public async Task PersistStateAsync_ProtectsPersistedState() { // Arrange var expected = @"{""MyValue"":""AQIDBA==""}"; var store = new ProtectedPrerenderComponentApplicationStore(_provider); var state = new Dictionary <string, byte[]>() { ["MyValue"] = new byte[] { 1, 2, 3, 4 } }; // Act await store.PersistStateAsync(state); // Assert Assert.Equal(expected, _protector.Unprotect(store.PersistedState)); }
// InitializeAsync is used in a fire-and-forget context, so it's responsible for its own // error handling. public Task InitializeAsync(ProtectedPrerenderComponentApplicationStore store, CancellationToken cancellationToken) { Log.InitializationStarted(_logger); return(Renderer.Dispatcher.InvokeAsync(async() => { if (_initialized) { throw new InvalidOperationException("The circuit host is already initialized."); } try { _initialized = true; // We're ready to accept incoming JSInterop calls from here on await OnCircuitOpenedAsync(cancellationToken); await OnConnectionUpAsync(cancellationToken); // We add the root components *after* the circuit is flagged as open. // That's because AddComponentAsync waits for quiescence, which can take // arbitrarily long. In the meantime we might need to be receiving and // processing incoming JSInterop calls or similar. var count = Descriptors.Count; for (var i = 0; i < count; i++) { var(componentType, parameters, sequence) = Descriptors[i]; await Renderer.AddComponentAsync(componentType, parameters, sequence.ToString(CultureInfo.InvariantCulture)); } // At this point all components have successfully produced an initial render and we can clear the contents of the component // application state store. This ensures the memory that was not used during the initial render of these components gets // reclaimed since no-one else is holding on to it any longer. store.ExistingState.Clear(); Log.InitializationSucceeded(_logger); } catch (Exception ex) { // Report errors asynchronously. InitializeAsync is designed not to throw. Log.InitializationFailed(_logger, ex); UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false)); await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex), ex); } })); }