Background
This blog describes two things.
- How to store session data out of the ASP.NET Core process
- How to separate the life cycle of session data from that of ASP.NET Core process and host
In the background, ASP.NET Core app can run as a container (docker) architecture. When ASP.NET Core app runs as a docker, it's easy to scale up and down. In this situation, we would like to store session data out of Docker containers and separate lifecycle. However, you may think if we can store session data out of the ASP.NET Core process, we can separate the lifecycle of session data from that of ASP.NET Core process and host at the same time. However, this is not necessarily so at present ASP.NET Core. I explain it based on the Microsoft's document.
Managing Application State | Microsoft Docs
Configure IDistributedCache to store session data out of memory
At first, we should have to configure a service implementing IDistributedCache
. The above document introduces Microsoft.Extensions.Caching.Memory
for development purpose. However, we would like to other services for storing session data out of ASP.NET Core process. ASP.NET Core team provides two packages for this purpose. One is using Redis and the other is using SQLServer.
I'm using Redis at present. After adding Microsoft.Extensions.Caching.Redis
in project.json, you may just configure Startup class like below. I use the code samples described in the above Microsoft document to confirm session is working.
When you run this code with Redis-server, you will find the session is stored in Redis and session is working like below.
However, a session will be lost in this sample after ASP.NET Core process runs on another machine or container even if the Redis-server keeps running. Why is this?
Configure IDataProtection to separate the life cycle
The key to solving this issue is IDataProtection
. ASP.NET Core stores session with encrypted by default. And the encrypted key is generated and stored at each machine without any configuration. Microsoft.AspNetCore.Session
packages depends on Microsoft.AspNetCore.DataProtection
. Here is the code Microsoft.AspNetCore.DataProtection
generates and stored the encryption key in the local filesystem.
DataProtection/FileSystemXmlRepository.cs at rel/1.1.0 · aspnet/DataProtection · GitHub
So if you run the above code sample, you will find the encryption key under ~/.aspnet
folder. Because the encryption key is different from the machine, a new machine can't decrypt the session data and the session will be lost even if the HTTP request has the same cookie value which client send.
To prevent this issue, we can use other IDataProtection
implementations which store encryption key out of the machine. ASP.NET Core team provides two packages at present. One is Redis, the other is AzureStorage.
As I use Redis to store session, I also want to store encryption key into Redis.
It's easy to use Microsoft.AspNetCore.DataProtection.Redis
. Like IDistributedCache
, simply add Microsoft.AspNetCore.DataProtection.Redis
in project.json and configure Startup class like below.
After running this code sample, you will find the key in Redis.
Also, of course, session won't be lost even if the ASP.NET Core process is scaled up with other machines and also scaled down. You can find full code example here.