This repository contains a minimal ASP.NET Core example implementing a "lazy" distributed session middleware that avoids contacting the remote distributed cache unless session data actually exists.
- Only create/store a session id and remote data when values are written.
- Do not query the distributed cache for reads if there was no session cookie on the incoming request.
- Remove session cookie and remote entry when session data is cleared.
- Reuse existing ASP.NET Core primitives where possible (
ISession,ISessionFeature,SessionOptions.Cookie.Build). - Reduce allocations where straightforward by using
ValueTaskfor internal helpers.
src/CustomSession/LazySession.cs—ISessionimplementation that defers loading fromIDistributedCachewhen no cookie exists and only writes to cache when modified.src/CustomSession/LazySessionMiddleware.cs— middleware that installs anISessionFeaturebacked byLazySession, commits changes at the end of the request, and manages the session cookie usingSessionOptions.Cookie.Build(HttpContext).src/CustomSession/SessionExtensions.cs—UseLazyDistributedSession()extension to register the middleware.src/CustomSession/ValueTaskExtensions.cs— small helper utilities for working withValueTaskresults.src/CustomSession/Program.cs— minimal example app showing how to register the distributed cache and configureSessionOptions.src/CustomSession.Tests— xUnit tests that mockIDistributedCacheand verify the middleware behavior (no cache calls when no cookie and no access; store when setting values; remove and delete cookie when clearing a session that had a cookie).
- Lazy load behavior: when the request has no session cookie, the session starts empty and never queries the distributed cache. If the application writes to the session, a session id is generated and
IDistributedCache.SetAsyncis called on commit. - Cookie management: when the session ends with data, we append a cookie produced by
SessionOptions.Cookie.Build(HttpContext). If the request started with a cookie but the session ends empty (cleared or no values), the middleware removes the remote entry and deletes the cookie using the same built options (so path/SameSite/etc. match). - ValueTask usage: internal helpers use
ValueTaskto reduce allocations when operations complete synchronously. PublicISessionmethods still exposeTaskper interface and convert viaAsTask().
- In
Program.csconfigure a distributed cache and session options:
builder.Services.AddDistributedMemoryCache(); // or Redis, SQL, etc.
builder.Services.Configure<SessionOptions>(opts => {
opts.Cookie.Name = ".AspNetCore.Session";
opts.IdleTimeout = TimeSpan.FromMinutes(20);
});
app.UseLazyDistributedSession();- Use
HttpContext.Sessionas usual inside request handlers. The middleware will ensure the session is only materialized in the remote store if you write values.
Run tests from the repository root:
dotnet test src/CustomSession/CustomSession.slnThe tests mock IDistributedCache to verify the following scenarios:
- No cookie and no session access -> no cache calls.
- Setting a session value ->
SetAsynccalled andSet-Cookieheader produced. - Request with incoming cookie, then clearing session ->
RemoveAsynccalled and cookie deleted.
- Replace
AddDistributedMemoryCache()withAddStackExchangeRedisCache(...)or a SQL implementation as appropriate. - The middleware uses
IDistributedCacheso any registered implementation will be used.
- Add more tests verifying expiry and cookie attributes.
- Support
ISessionconcurrency semantics more closely to the reference implementation (locking/refresh semantics) if needed. - Consider exposing configuration options for cookie naming/creation behavior via options on the middleware.