-
Notifications
You must be signed in to change notification settings - Fork 10.3k
[Blazor] Apply persistence api review feedback #31147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,26 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using System.Buffers; | ||
|
||
namespace Microsoft.AspNetCore.Components.Lifetime | ||
namespace Microsoft.AspNetCore.Components | ||
{ | ||
/// <summary> | ||
/// Manages the storage for components and services that are part of a Blazor application. | ||
/// </summary> | ||
public interface IComponentApplicationStateStore | ||
public interface IPersistentComponentStateStore | ||
{ | ||
/// <summary> | ||
/// Gets the persisted state from the store. | ||
/// </summary> | ||
/// <returns>The persisted state.</returns> | ||
Task<IDictionary<string, byte[]>> GetPersistedStateAsync(); | ||
Task<IDictionary<string, ReadOnlySequence<byte>>> GetPersistedStateAsync(); | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// Persists the serialized state into the storage. | ||
/// </summary> | ||
/// <param name="state">The serialized state to persist.</param> | ||
/// <returns>A <see cref="Task" /> that completes when the state is persisted to disk.</returns> | ||
Task PersistStateAsync(IReadOnlyDictionary<string, byte[]> state); | ||
Task PersistStateAsync(IReadOnlyDictionary<string, ReadOnlySequence<byte>> state); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,56 +2,60 @@ | |
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Buffers; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.IO.Pipelines; | ||
javiercn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Components.RenderTree; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Microsoft.AspNetCore.Components.Lifetime | ||
namespace Microsoft.AspNetCore.Components.Infrastructure | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this an existing namespace? Seems strange for something with public types in it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
/// <summary> | ||
/// Manages the lifetime of a component application. | ||
/// Manages the persistent state of components in an application. | ||
/// </summary> | ||
public class ComponentApplicationLifetime | ||
public class ComponentStatePersistenceManager : IDisposable | ||
{ | ||
private bool _stateIsPersisted; | ||
private readonly List<ComponentApplicationState.OnPersistingCallback> _pauseCallbacks = new(); | ||
private readonly Dictionary<string, byte[]> _currentState = new(); | ||
private readonly ILogger<ComponentApplicationLifetime> _logger; | ||
private readonly List<Func<Task>> _pauseCallbacks = new(); | ||
private readonly Dictionary<string, PooledByteBufferWriter> _currentState = new(StringComparer.Ordinal); | ||
private readonly ILogger<ComponentStatePersistenceManager> _logger; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of <see cref="ComponentApplicationLifetime"/>. | ||
/// Initializes a new instance of <see cref="ComponentStatePersistenceManager"/>. | ||
/// </summary> | ||
public ComponentApplicationLifetime(ILogger<ComponentApplicationLifetime> logger) | ||
public ComponentStatePersistenceManager(ILogger<ComponentStatePersistenceManager> logger) | ||
{ | ||
State = new ComponentApplicationState(_currentState, _pauseCallbacks); | ||
State = new PersistentComponentState(_currentState, _pauseCallbacks); | ||
_logger = logger; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the <see cref="ComponentApplicationState"/> associated with the <see cref="ComponentApplicationLifetime"/>. | ||
/// Gets the <see cref="ComponentStatePersistenceManager"/> associated with the <see cref="ComponentStatePersistenceManager"/>. | ||
/// </summary> | ||
public ComponentApplicationState State { get; } | ||
public PersistentComponentState State { get; } | ||
|
||
/// <summary> | ||
/// Restores the component application state from the given <see cref="IComponentApplicationStateStore"/>. | ||
/// Restores the component application state from the given <see cref="IPersistentComponentStateStore"/>. | ||
/// </summary> | ||
/// <param name="store">The <see cref="IComponentApplicationStateStore"/> to restore the application state from.</param> | ||
/// <param name="store">The <see cref="IPersistentComponentStateStore"/> to restore the application state from.</param> | ||
/// <returns>A <see cref="Task"/> that will complete when the state has been restored.</returns> | ||
public async Task RestoreStateAsync(IComponentApplicationStateStore store) | ||
public async Task RestoreStateAsync(IPersistentComponentStateStore store) | ||
{ | ||
var data = await store.GetPersistedStateAsync(); | ||
State.InitializeExistingState(data); | ||
} | ||
|
||
/// <summary> | ||
/// Persists the component application state into the given <see cref="IComponentApplicationStateStore"/>. | ||
/// Persists the component application state into the given <see cref="IPersistentComponentStateStore"/>. | ||
/// </summary> | ||
/// <param name="store">The <see cref="IComponentApplicationStateStore"/> to restore the application state from.</param> | ||
/// <param name="store">The <see cref="IPersistentComponentStateStore"/> to restore the application state from.</param> | ||
/// <param name="renderer">The <see cref="Renderer"/> that components are being rendered.</param> | ||
/// <returns>A <see cref="Task"/> that will complete when the state has been restored.</returns> | ||
public Task PersistStateAsync(IComponentApplicationStateStore store, Renderer renderer) | ||
public Task PersistStateAsync(IPersistentComponentStateStore store, Renderer renderer) | ||
{ | ||
if (_stateIsPersisted) | ||
{ | ||
|
@@ -64,18 +68,31 @@ public Task PersistStateAsync(IComponentApplicationStateStore store, Renderer re | |
|
||
async Task PauseAndPersistState() | ||
{ | ||
State.PersistingState = true; | ||
await PauseAsync(); | ||
State.PersistingState = false; | ||
|
||
var data = new Dictionary<string, ReadOnlySequence<byte>>(StringComparer.Ordinal); | ||
foreach (var (key, value) in _currentState) | ||
{ | ||
data[key] = new ReadOnlySequence<byte>(value.WrittenMemory); | ||
} | ||
|
||
var data = new ReadOnlyDictionary<string, byte[]>(_currentState); | ||
await store.PersistStateAsync(data); | ||
|
||
foreach (var value in _currentState.Values) | ||
{ | ||
value.Dispose(); | ||
} | ||
_currentState.Clear(); | ||
} | ||
} | ||
|
||
internal Task PauseAsync() | ||
{ | ||
List<Task>? pendingCallbackTasks = null; | ||
|
||
for (int i = 0; i < _pauseCallbacks.Count; i++) | ||
for (var i = 0; i < _pauseCallbacks.Count; i++) | ||
{ | ||
var callback = _pauseCallbacks[i]; | ||
var result = ExecuteCallback(callback, _logger); | ||
|
@@ -95,7 +112,7 @@ internal Task PauseAsync() | |
return Task.CompletedTask; | ||
} | ||
|
||
static Task ExecuteCallback(ComponentApplicationState.OnPersistingCallback callback, ILogger<ComponentApplicationLifetime> logger) | ||
static Task ExecuteCallback(Func<Task> callback, ILogger<ComponentStatePersistenceManager> logger) | ||
{ | ||
try | ||
{ | ||
|
@@ -115,7 +132,7 @@ static Task ExecuteCallback(ComponentApplicationState.OnPersistingCallback callb | |
return Task.CompletedTask; | ||
} | ||
|
||
static async Task Awaited(Task task, ILogger<ComponentApplicationLifetime> logger) | ||
static async Task Awaited(Task task, ILogger<ComponentStatePersistenceManager> logger) | ||
{ | ||
try | ||
{ | ||
|
@@ -129,5 +146,14 @@ static async Task Awaited(Task task, ILogger<ComponentApplicationLifetime> logge | |
} | ||
} | ||
} | ||
|
||
void IDisposable.Dispose() | ||
{ | ||
foreach (var value in _currentState.Values) | ||
{ | ||
value.Dispose(); | ||
} | ||
_currentState.Clear(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
<Compile Include="$(ComponentsSharedSourceRoot)src\JsonSerializerOptionsProvider.cs" /> | ||
<Compile Include="$(SharedSourceRoot)LinkerFlags.cs" LinkBase="Shared" /> | ||
<Compile Include="$(SharedSourceRoot)QueryStringEnumerable.cs" LinkBase="Shared" /> | ||
<Compile Include="$(RepoRoot)src\Shared\Components\PooledByteBufferWritter.cs" LinkBase="Infrastructure" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this single file appear in three different pseudo-folders❔ Suggest using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The inconsistency here leads me to suggest we should fix before this is merged or back-ported |
||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
|
Uh oh!
There was an error while loading. Please reload this page.