Accessing ASP.NET Framework Sessions from ASP.NET Core
The Scaleout.AspNetCore.SessionInterop NuGet package provides an API for ASP.NET Core applications to access traditional ASP.NET Framework session state objects that are stored in the ScaleOut service.
Prerequisites
One or more instances of the ScaleOut SessionServer or StateServer service (either local or remote).
.NET Core (6, 7, or higher).
Configuration
Traditional ASP.NET Framework Configuration
The ASP.NET Framework 4.x web app must be configured to use either the original session provider (Soss.Web.SossStoreProvider) that ships in the ScaleOut Product Suite’s installer or one of the newer NuGet-based session providers (Scaleout.AspNet/Scaleout.AspNetAsync).
Whichever provider you use, the name of the namespace holding the session objects must be explicitly set, as described here:
Soss.Web.SossStoreProvider
If using the original session provider (Soss.Web.SossStoreProvider) that ships in the ScaleOut Product Suite, set the soss_SharedAppName AppSetting value in your app’s web.config file:
<configuration>
<appSettings>
<add key="soss_SharedAppName" value="MySharedSessionNamepace"/>
</appSettings>
<!-- ... -->
</configuration>
Scaleout.AspNet and Scaleout.AspNetAsync
If the legacy app uses one of the newer NuGet-based session providers (Scaleout.AspNet or Scaleout.AspNetAsync), set the session provider’s namespace
attribute to an explicit value in web.config:
<sessionState mode="Custom" customProvider="ScaleOutSessionProvider" timeout="20" regenerateExpiredSessionId="true">
<providers>
<add name="ScaleOutSessionProvider"
type="Scaleout.AspNet.ScaleoutSessionStateProvider"
connectionString="bootstrapGateways=192.168.1.42:721,192.168.1.43:721;maxRequestRetries=2;eventConnectionCount=4"
clientCacheType="LRU"
clientCacheCapacity="100"
encryptionPasswordLocation="None"
handleSessionEnd="false"
namespace="MySharedSessionNamepace" />
</providers>
</sessionState>
ASP.NET Core/6/7+ Configuration
Install this NuGet package and its dependencies:
Install-Package Scaleout.AspNetCore.SessionInterop
Modify your csproj file to allow use of the BinaryFormatter to deserialize objects from the legacy application.
<PropertyGroup> <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization> </PropertyGroup>
Note
Traditional ASP.NET applications use the BinaryFormatter for session serialization, but the BinaryFormatter was deprecated in .NET 5 due to security concerns: Exploits are possible when an application deserializes untrusted data (in other words, if you allow users to serialize objects themselves and upload them to your servers). However, the serialization performed by ASP.NET’s session pipeline occurs entirely within the confines of your web server. In this case, no untrusted objects are deserialized, and the BinaryFormatter can be used with a reasonable degree of safety.
In your application’s startup code, connect to the ScaleOut service by providing a connection string to a
GridConnection
:// Get connection string from config: string connString = builder.Configuration.GetSection("scaleoutConnString").Value; var conn = GridConnection.Connect(connString);
Create an instance of the
LegacySessionAccessor
class and register it with dependency injection as a singleton:builder.Services.AddSingleton<ILegacySessionAccessor, LegacySessionAccessor>(_ => { string connString = builder.Configuration.GetSection("scaleoutConnString").Value; var conn = GridConnection.Connect(connString); return new LegacySessionAccessor( gridConnection: conn, sharedAppName: "MySharedSessionNamepace", interopMode: InteropMode.SossStoreProvider, defaultSessionTimeoutMins: 20, maxSerializedCacheMemory: 10_000_000, keyEncoder: null); });
The constructor takes the following arguments:
- gridConnection
The GridConnection instance discussed above.
- sharedAppName
The namespace name that the traditional ASP.NET app uses to store its session objects.
- interopVersion
An enum indicating the type of session provider used by the traditional ASP.NET Framework 4.x app. Pass one of the following options:
InteropMode.SossStoreProvider: The original session provider (Soss.Web.SossStoreProvider) that ships in the ScaleOut Product Suite’s installer.
InteropMode.ScaleoutAspNet: NuGet-based session provider (Scaleout.AspNet/Scaleout.AspNetAsync).
- defaultSessionTimeoutMins
The number of minutes the session can be idle before it is abandoned. This value should match the timeout value in the legacy app’s <sessionState/> web.config element.
- maxSerializedCacheMemory
(Optional) The size (in bytes) of the in-process network cache that is used to improve performance when retrieving objects. The default value is 10,000,000 bytes (10 megabytes).
- keyEncoder
(Optional) A custom
Scaleout.Client.KeyEncoding.KeyEncoder
instance that can be registered when the legacy ASP.NET uses a custom SessionIdManager. (Advanced users only, default isnull
.)
Accessing Legacy Session Objects from a Controller
Once the LegacySessionAccessor is registered with the .NET Core DI system, it can be used in a controller to access the session dictionary of the ASP.NET Framework app. The session ID of the legacy app must be extracted from the request’s cookies and passed into the LegacySessionAccessor methods. By default, ASP.NET Framework 4.x apps store the session ID in a cookie named ASP.NET_SessionId
:
string? legacySessionId = Request.Cookies["ASP.NET_SessionId"];
if (legacySessionId == null)
{
// User does not have a session in the legacy app.
return;
}
Reading Session Objects
Use the ID in this session cookie to access the session dictionary object. If an object does not exist, the Retrieve method will return null. A new instance of a session dictionary can be created using LegacySessionAccessor.NewEmpty()
.
var legacySessionDict = _sessionAccessor.Retrieve(legacySessionId);
if (legacySessionDict == null)
{
// Session expired or has been removed. Create a new instance:
legacySessionDict = _sessionAccessor.NewEmpty();
}
// Get a value from the dictionary:
string? favoriteColor = legacySessionDict["Favorite Color"] as string;
Persisting Modifications
Changes to objects in the session dictionary are not automatically persisted at the end of the request. To save changes to the session dictionary call Commit or CommitAsync:
// Modify the dictionary:
legacySessionDict["Favorite Color"] = "Blue";
// Persist changes to the ScaleOut service:
_sessionAccessor.Commit(legacySessionId, legacySessionDict);
Changing a Session’s Idle Timeout
The Commit method provides an overload that allows a session’s idle timeout to be changed:
_sessionAccessor.Commit(legacySessionId, legacySessionDict, newTimeout: TimeSpan.FromMinutes(30));
Abandoning a Session
A user’s session can be deleted from the ScaleOut service using the Abandon method:
_sessionAccessor.Abandon(legacySessionId);
Full Example
using Microsoft.AspNetCore.Mvc.RazorPages;
using Scaleout.AspNetCore.SessionInterop;
namespace SessionInteropSample.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
ILegacySessionAccessor _sessionAccessor;
public IndexModel(ILogger<IndexModel> logger, ILegacySessionAccessor legacySessionAccessor)
{
_logger = logger;
_sessionAccessor = legacySessionAccessor;
}
public void OnGet()
{
string? legacySessionId = Request.Cookies["ASP.NET_SessionId"];
if (legacySessionId == null)
{
// User does not have a session in the legacy app.
return;
}
var legacySessionDict = _sessionAccessor.Retrieve(legacySessionId);
if (legacySessionDict == null)
{
// Session expired or has been removed. Create a new instance:
legacySessionDict = _sessionAccessor.NewEmpty();
}
// Get a value from the dictionary:
string? favoriteColor = legacySessionDict["Favorite Color"] as string;
// Modify the dictionary:
legacySessionDict["Favorite Color"] = "Blue";
// Persist changes to the ScaleOut service:
_sessionAccessor.Commit(legacySessionId, legacySessionDict);
}
}
}