You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds Csla.Blazor.State.AzureTableStorage, an opt-in NuGet package that implements ISessionStore (#4832) on Azure Table Storage. Discussed and agreed in #4867.
The store is only used when UseInMemoryApplicationContextManager is false. Call UseAzureTableStorageSessionStore after AddServerSideBlazor; it replaces the in-memory store registration.
The TableServiceClient comes from ConnectionString if set, otherwise from a keyed service (ServiceKey, for example one registered by Aspire), otherwise from the default TableServiceClient in the container. The package never registers a client itself, so it does not change which client the rest of the app receives.
Behavior
One row per session: PartitionKey is the session key, and RowKey is ApplicationDiscriminator, so several apps can share a table.
Live sessions are kept in memory and returned by reference, as InMemorySessionStore does, because SessionManager and ApplicationContextManagerBlazor change the returned session in place. The table is read only when a session is not in memory, such as after a restart.
Session data is written to the table by AddOrUpdateSessionAsync, for example when the WebAssembly client sends its state, under a per-key lock so the cache and the table agree. Changes made in place reach the table with the next such write.
PurgeSessions refreshes only the LastTouched column of live sessions, so other servers do not purge them. It never serializes them, because a change a request makes during serialization can produce data that cannot be deserialized. Apps should call it periodically.
Session data is split across up to 11 binary columns of 64 KiB (704 KiB), because Azure Tables limits a binary property to 64 KiB. A larger session fails with InvalidOperationException before anything is written or cached.
With more than one server, each user must stay on the same server, as Blazor Server already requires.
Read and write failures propagate, so a failed read can't be mistaken for a missing session.
Purge is best effort and deletes stale rows using the ETag from its query.
The table is created on first use if it does not exist.
Business objects restored from the table keep the ApplicationContext of the scope that deserialized them, as sessions that arrive through StateController.Put do today. Fixing that needs the caller's context, so it is left for a core change.
The in-memory cache could instead live in SessionManager, so every ISessionStore gets it. I can move it there if you prefer.
Adds Csla.Blazor.State.AzureTableStorage, an opt-in ISessionStore for
Blazor state management backed by Azure Table Storage, enabled with
UseAzureTableStorageSessionStore() after AddServerSideBlazor().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ssion store
Keep live sessions in memory and return them by reference, because
SessionManager and ApplicationContextManagerBlazor change the returned
session in place. The table is read on a cache miss, written on every
AddOrUpdateSessionAsync, and written again for live sessions during
purge. Purge deletes stale rows using the queried ETag.
Resolve the serialization formatter in a new scope per operation,
since it depends on the scoped ApplicationContext and keeps per-call
state.
Register with AddSingleton, register the options in DI, make the store
and SessionEntity internal, log through LoggerMessage methods, and use
an injected TimeProvider for the purge cutoff.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Azure Table Storage limits a binary property to 64 KiB. Putting the entire serialized session in Value therefore makes sessions above that size fail on every write, even when they fit within the overall entity-size limit. Support larger payloads with bounded chunks and reassembly, or explicitly document and validate the serialized-size limit before publishing the session to the cache. Add boundary tests; if larger sessions are supported, include a round trip exceeding 64 KiB.
… large sessions
Purge serialized sessions that requests change in place, which could
write data that cannot be deserialized, and could overwrite a newer
session saved for the same key. Purge now refreshes only the stored
LastTouched column of live sessions. AddOrUpdateSessionAsync serializes,
writes, and updates the cache under a per-key lock, publishing the
session only after the write succeeds.
Azure Tables limits a binary property to 64 KiB, so session data is
split across up to 11 columns (704 KiB). Larger sessions fail with
InvalidOperationException before anything is written or cached.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
I'll review it this weekend. Sorry for taking so long. 😅
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
Csla.Blazor.State.AzureTableStorage, an opt-in NuGet package that implementsISessionStore(#4832) on Azure Table Storage. Discussed and agreed in #4867.Usage
The store is only used when
UseInMemoryApplicationContextManagerisfalse. CallUseAzureTableStorageSessionStoreafterAddServerSideBlazor; it replaces the in-memory store registration.The
TableServiceClientcomes fromConnectionStringif set, otherwise from a keyed service (ServiceKey, for example one registered by Aspire), otherwise from the defaultTableServiceClientin the container. The package never registers a client itself, so it does not change which client the rest of the app receives.Behavior
PartitionKeyis the session key, andRowKeyisApplicationDiscriminator, so several apps can share a table.InMemorySessionStoredoes, becauseSessionManagerandApplicationContextManagerBlazorchange the returned session in place. The table is read only when a session is not in memory, such as after a restart.AddOrUpdateSessionAsync, for example when the WebAssembly client sends its state, under a per-key lock so the cache and the table agree. Changes made in place reach the table with the next such write.PurgeSessionsrefreshes only theLastTouchedcolumn of live sessions, so other servers do not purge them. It never serializes them, because a change a request makes during serialization can produce data that cannot be deserialized. Apps should call it periodically.InvalidOperationExceptionbefore anything is written or cached.Business objects restored from the table keep the
ApplicationContextof the scope that deserialized them, as sessions that arrive throughStateController.Putdo today. Fixing that needs the caller's context, so it is left for a core change.The in-memory cache could instead live in
SessionManager, so everyISessionStoregets it. I can move it there if you prefer.Tests
Csla.Blazor.State.AzureTableStorage.Tests(net10.0, net11.0):SkipOnCIServer. Locally:docker run -p 10002:10002 mcr.microsoft.com/azure-storage/azurite azurite-table --tableHost 0.0.0.0.All 25 pass locally on both target frameworks.
There is no backlog issue for this; the commit references the discussion.
🤖 Generated with Claude Code