WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

Session 情報のストアに SQL Server を利用 (CORE)

by WebSurfer 13. March 2022 15:52

ASP.NET Core アプリで Session 状態を利用する際に SQL Server をストアとして利用する方法を書きます。下の画像の赤線部分は Session 情報を Controller で ViewBag に設定しそれを表示したものです。

Session 状態の表示

ASP.NET Core アプリでの Session の使い方の詳しい話は Microsoft のドキュメント「ASP.NET Core でのセッションと状態の管理」にあります。

それを読めば大体分かると思いますが、.NET Framework 版の ASP.NET アプリの Session との大きな違いで自分が気が付いた点を以下にまとめておきます。

  1. Program.cs (.NET 6.0) または Startup.cs (.NET 5.0 以前) でサービスとミドルウェアの登録が必要。
  2. ASP.NET Core の分散キャッシュをデータのストアに利用する。 (なので、メモリ内キャッシュを使用する場合であってもセッションデータはシリアル化可能なでなければならない)
  3. String 型、Int32 型、Byte[] 型以外のデータ型は格納できない。(String 型、Int32 型、Byte[] 型以外のデータを格納する場合は JSON 文字列にシリアライズするなどの方法で格納する)
  4. 同時アクセスでもロックされず後書き優先になる。 (先の記事「SessionStateModule によるロック」で書いたロック機能は提供されてない)
  5. StateServer モードに相当する機能は「分散 Redis キャッシュ」を使って実現するらしい。(未検証・未確認です)

上の 2 に書いたように Session 情報のストアには分散キャッシュを利用するのですが、それには以下があるそうです。詳しくは Microsoft のドキュメント「ASP.NET Core の分散キャッシュ」を見てください。

  • 分散メモリ キャッシュ - AddDistributedMemoryCache
  • 分散 SQL Server キャッシュ - AddDistributedSqlServerCache
  • 分散 Redis キャッシュ - AddStackExchangeRedisCache
  • 分散 NCache キャッシュ - AddNCacheDistributedCache

上の「分散メモリキャッシュ」というのは、サーバーで実行されている Web アプリのメモリを使って ASP.NET Core の分散キャッシュを実現するためのもので、実際に「分散」されているわけではないそうです。

「分散メモリキャッシュ」を利用しての Session 状態の構成方法や使い方は Microsoft のドキュメント「ASP.NET Core でのセッションと状態の管理」に詳しく書いてありますのでそれを読めば容易に実装できると思います。

この記事では「分散 SQL Server キャッシュ」を利用しての Session 状態の構成方法を書きます。以下の説明と合わせて、Microsoft のドキュメントの「分散 SQL Server キャッシュ」のセクションも見てください。

(1) SQL Server のテーブルを作成

sql-cache create コマンドを実行して SQL Server にテーブルを作成します。以下の点に注意してください。

  • dotnet-sql-cache ツールをインストールしておく必要があります。dotnet tool install --global dotnet-sql-cache コマンドでインストールできます。
  • コマンドの接続文字列の Initial Catalog に設定するデータベースは事前に作成しておく必要があります。その際、sql-cache create コマンドを実行するユーザーのアカウントが SQL Server のログインに設定されていて、そのデータベースに対してテーブルを作成する権限を持っている必要があります。

この記事では開発マシンの SQL Server 2012 Express の既存のデータベース TestDatabase に Windows 認証で接続し、TestCache という名前でテーブルを作成しました。その結果は以下の画像の通りです。

SQL Server のテーブル

Microsoft ドキュメントの説明とは Id の Data Type が nvarchar(900) ではなくて nvarchar(499) と異なりますが、その他は同じ内容で TeestCache テーブルが生成されています。

(2) NuGet パッケージのインストール

NuGet パッケージ Microsoft.Extensions.Caching.SqlServer をインストールします。下の (3) 項でのサービスの登録で AddDistributedSqlServerCache を使うのに必要になります。

Microsoft.Extensions.Caching.SqlServer

(3) サービスとミドルウェアの登録

Program.cs (.NET 6.0) または Stratup.cs (.NET 5.0 以前) でサービスとミドルウェアを登録します。以下の例は Visual Studio 2022 で作成した .NET 6.0 の Program.cs での例です。

// ・・・前略・・・

// SQL Server 分散キャッシュを使用
// "DistCacheConnectionString" は appsettings.json に設定する
// SQL Server への接続文字列
builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = 
        builder.Configuration.GetConnectionString(
                               "DistCacheConnectionString");
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

// AddSession の呼び出し
builder.Services.AddSession(options =>
{
    // デフォルトは 20 分。検証用に 300 秒に設定
    options.IdleTimeout = TimeSpan.FromSeconds(300);

    options.Cookie.HttpOnly = true;  // デフォルトで true

    // デフォルトは false。true に設定しないとセッション状態は
    // 機能しない可能性あり
    options.Cookie.IsEssential = true;  
});

// ・・・中略・・・

// UseSession の呼び出し
// 順序が重要で、UseRouting の後かつ MapRazorPages と 
// MapControllerRoute の前に呼び出します
app.UseSession();

// ・・・後略・・・

(4) appsettings.json に接続文字列を設定

上の (3) 項の AddDistributedSqlServerCache メソッドで指定した "DistCacheConnectionString" という名前で構成ファイルから接続文字列を取得できるよう、appsettings.json ファイルに以下の設定を追加します。

"ConnectionStrings": {
  "DistCacheConnectionString": 
    "Data Source=(local)\\sqlexpress;Initial Catalog=TestDatabase;Integrated Security=True;"
}

上の接続文字列は SQL Server Express の sqlexpress という名前付きインスタンスにアタッチされている TestDatabase というデータベースに Windows 認証でローカルに接続するものです。そのあたりは個人の環境に合わせて変更してください。


以上で Session 状態は使えるようになります。

例えば HomeController で以下のように Session を設定して実行し、Home/Privacy に遷移した後で Home/Index に戻るとこの記事の一番上の画像の赤線で示したように Session データが表示されます。

public IActionResult Index()
{
    // Session は home/privacy で設定
    ViewBag.Name = HttpContext.Session.GetString("name");
    ViewBag.Test = HttpContext.Session.GetString("test");
    HttpContext.Session.Clear();

    return View();
}

public IActionResult Privacy()
{
    // Session の設定
    HttpContext.Session.SetString("name", "session value");
    HttpContext.Session.SetString("test", "test value");

   return View();
}

SQL Server に格納された Session データは以下のようになります。Chrome と Edge を立ち上げて両方からアクセスした結果で、それぞれ Session Id (デフォルトで .AspNetCore.Session という名前の cookie に格納されている) が異なるので、TestCache テーブルには Id が異なるレコードが 2 つ存在するという結果になっています。

SQL Server に格納された Session データ

上の画像のレコードはクライアントがブラウザを閉じてもそのまま残ります。と言っても、それが再利用されることはなく、再度ブラウザを立ち上げてアクセスして Session を使うと別の Id でレコードが追加されます。

上の Home/Index のコードにある HttpContext.Session.Clear() でも SQL Server のレコードは削除されません。Session Cookie も削除されません。Value の中のデータが削除され、ExpiresAtTime がその時点から AddSession メソッドの options.IdleTimeout で設定された時間まで延長されるのみです。

サーバー側で Session を使っているユーザーがオンラインか否かを判定する術はなく、サーバーではレコード削除の可否を判断できないのでそうせざるを得ないのではないかと思います。

ということはレコードは削除されずどんどん増えていく一方になるのではと思いましたが、翌日にまた調べてみたら、SQL Server には昨日のレコードは残っていたが、アプリを動かしてみると昨日のレコードは消えました。どうやら、アプリを起動する際(多分 Program.cs のコードが実行される時)に ExpiresAtTime が古いレコードは消去されるらしいです。

IIS を使ってのインプロセスホスティングモデルであればワーカープロセスがリサイクルされる時に Program.cs のコードが実行される(古いレコードは削除される)と思いますが、それ以外のホスティングモデルで Kestrel が使われる時はどうなるかは分かりません。そこは今後の検討課題ということにしたいと思います。

Tags: , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。その後ブログ2を追加し、ここはプログラミング関係、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  July 2022  >>
MoTuWeThFrSaSu
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

View posts in large calendar