dotnet / dotnet/aspnetcore

Introduce a session metadata to load session asynchronously in session middleware

Open
#35,154 8 comments 0 reactions 0 assignees View on GitHub
api-suggestion area-middleware feature-session
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 10h
Merged PRs (30d)
281

Description

## Background and Motivation
The default session provider in ASP.NET Core loads session records from the underlying IDistributedCache backing store asynchronously only if the ISession.LoadAsync method is explicitly called before the TryGetValue, Set, or Remove methods. If LoadAsync isn't called first, the underlying session record is loaded synchronously, which can incur a performance penalty at scale.

We could provide a new endpoint metadata with `SessionState` property for session which controls the behavior of `SessionMiddleware`.

- SessionState.Default: The current behavior which creates a session key, set the session feature, set cookie if needed.
- SessionState.EagerLoad: Calls LoadAsync in session middleware [Needs a better name than EagerLoad!]
- SessionState.Disabled: Do nothing in session middleware, not even setting the session feature
- SessionState.ReadOnly: Calls LoadAsync in session middleware, but not CommitAsync and wrap ISession in a class which throw exception when Set, Remove or Clear called.

## Proposed API
```diff
public class SessionOptions
{
// The behavior of session middleware when `ISessionStateMetadata` is not available.
+ public SessionState DefaultBehavior { get; set; }
}
```
```csharp
public interface ISessionStateMetadata
{
SessionState State { get; }
}

public enum SessionState
{
Default = 0,
EagerLoad = 1,
Disabled = 2,
ReadOnly = 3,
}

public class SessionStateAttribute : ISessionStateMetadata
{
SessionState State { get; }
}

public static class RoutingEndpointConventionBuilderExtensions
{
public static TBuilder SetSessionBehvior(this TBuilder builder, SessionState sessionState) where TBuilder : IEndpointConventionBuilder;
}
```
## Usage Examples

``` C#
[SessionState(SessionState.Disabled)]
public class CustomerController
{
...
}

app.MapGet("/nosession", (HttpContext context) => $"Hello World!").SetSessionBehvior(SessionState.Disabled);

app.MapGet("/nosession2", [SessionState(SessionState.Disabled)] (HttpContext context) => $"Hello World!");

app.MapGet("/", (HttpContext context) => $"{context.Session.GetString("Hi")} World!").SetSessionBehvior(SessionState.ReadOnly);
```
## Risks
If Endpoints are marked by mistake the session would load for all requests even when they don't need session.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.