2023年10月23日月曜日

SQLite の妙と Dapper のシンプルさ

.NETでSQLite

System.Data.SQLite と Microsoft.Data.SQLite

 
.NET で SQLite データベースを扱う場合、通常は ADO.NET プロバイダーの  System.Data.SQLite を使うと思う。
だが、NuGet パッケージマネージャーで「Sqlite」を検索すると、もう一つ Microsoft.Data.SQLite というのが出てくる。
これは、Entity Framework のチームが .NET Core に合わせて作った、新しい SQLite の ADO.NET プロバイダーだそうだ。
この両者の違いは、以下のページに詳しい。


そしてご存じの通り、SQLite はデータ型の扱いが他の DBMS のような厳格なものではなく、かなりユルユルなのだが、.NET 側に変換されるのは、long (Int64)、double、string くらいだ。あっ DateTime があった。SQL 文で指定するときは文字列としてだが、.NET 側には  DateTime に変換される。
上で long と書いたが、そう、CREATE TABLE でデータ型を INTEGER として定義しても、.NET 側には long に変換される。
ところが!
上記で紹介したページにも書いてあるが、System.Data.SQLite の方は「追加のセマンティクスが列の型に適用され、」らしい。

これはどういうことかと言うと、INTEGER で定義した列のデータは long に変換されるが、INT で定義した列のデータは int に変換される。
論より証拠、サンプルプログラムを作成してみた。

テスト データベースの作成


まず適当なフォルダにテスト用データベース「test.db」を作成し、続けて以下の SQL 文を発行して「test1」テーブルを作成する。


3行目の列名「id_int」の型を INTEGER ではなく、INT にする。

※5行目の列名「name_nvarchar」の型 nvarchar は、.NET での照合順が正常に行われると、どこかで見た。。。けど忘れた。誰か知ってたら教えて。
ちなみに、SQLite は内部では UNICODE なので、nvarchar の「n」はあってもなくても同じこと。
※7行目の「value_float」の型 floatは、.NET に変換されるのは double で float に変換してくれないというテスト用。
※8行目の「value_double」の型 double は double を指定できるよ、というテスト用。

続けて、作成したテーブルにテスト データを追加しておく。


テスト プロジェクトの作成


C# コンソール プロジェクトを作成し、NuGet から System.Data.SQLite をインストールする。
テーブル「test1」に対応したクラス「Test1.cs」を作成する。


続けて「Program.cs」を以下のように書き換える。


これで実行すると、エラーなく結果が出力される。


このように、テーブル カラムを INTEGER として定義すると long に、INT として定義すると int に変換してくれる。そしてもちろんだが、列「id」の受け側のクラス メンバーを int にすると、エラーになる。
また、FLOAT として定義しても float にはしてくれず、double になってしまう。しかしこれは、REAL が SINGLE だということを考えれば、通常の動作ということになる。
ちなみに、float へ変換する際、上記のソースでは、(float)(double)dr["value_float"] としているが、エラーになる可能性がある。その場合は、以下のようにするとよろしい。


でも、この記事の主旨は、SQLite テーブル定義で、INT として列を定義すると、受け側の型を int にしておいても大丈夫、というものだが、果たしてどれだけの需要があるのか。。。
実際の業務では、このような曖昧なことをしてはいけない。後から(改修など)このテーブルを再定義する必要がある場合、「あっ!『INT』にしてるよ、前の担当者。直しとこ。」って INTEGER に直されたら大変なことになる。

実は、Dapper はこの辺りも吸収してくれて、尚且つシンプルになるので、かなりいい感じなんだなぁ。

ADO.NET から Dapper へ


上記では、Main 関数内で、foreach で行ごとにクラスへ変換しているが、Dapper を使うとシンプルになる。
また、DTO としてのデータ クラスのデータ型もある程度吸収してくれる。

テスト プロジェクトに NuGet から Dapper をインストールする。

Program.cs を以下のように書き換える。


実行時引数で引数なし、または 0 のときは、ADO.NET を、1 のときは、Dapper を使った処理になるようにした。
テーブルの行数を 10 万行、100 万行単位にして処理速度の比較テストを行う場合は、出力(コンソールやテキスト ファイルなど)をせず、最終形態であるデータ クラスのリストになるところまでで終了として、System.Diagnostics.Stopwatch で計測する。

見ての通り、Dapper は 47 行目の1行だけである。これで、列「id_int」も「value_float」もちゃんと変換してくれる。
あっぱれ!