はじめに
僕のお仕事では、担当の方にデスクトップアプリの EXE だけ送って、現場で入れ替えてもらうことも多いです。そんな時に使用する SQLite3 のマイグレーション処理を自前でシンプルに実装した際の備忘録です。
改訂履歴
- 2026/08/24 : 初版公開。
本文
1. 環境
- .NET 8.0
- Microsoft.Data.Sqlite.Core 10.0.11
- SQLite3 3.53.3
- SQLitePCLRaw.bundle_e_sqlite3 2.1.12
- Visual Studio Community 2022
2. 実装
SQLiteManager クラスに Migrate() を実装しました。
SQLiteManager.cs
using Microsoft.Data.Sqlite;
namespace SQLiteMigrationExample;
/// <summary>
/// SQLite データベースの管理を行います。
/// </summary>
public static class SQLiteManager
{
private static readonly int s_currentVersion = 3;
private static readonly string s_connectionString = "Data Source=DataBase.sqlite";
/// <summary>
/// データベースをマイグレーションします。
/// </summary>
/// <returns>非同期操作を表すタスク。マイグレーションに成功した場合は <see langword="true"/>、それ以外の場合は <see langword="false"/>。</returns>
public static async Task<bool> Migrate()
{
try
{
await using var connection = new SqliteConnection(s_connectionString);
await connection.OpenAsync();
// SQLite のスキーマバージョンを取得する。
await using SqliteCommand getCommand = connection.CreateCommand();
getCommand.CommandText = "PRAGMA user_version;";
var result = await getCommand.ExecuteScalarAsync();
if (result is null)
{
// ※必要に応じて異常処理を実装してください。
return false;
}
var version = Convert.ToInt32(result);
// マイグレーションを行う。
while (version < s_currentVersion)
{
using SqliteTransaction transaction = connection.BeginTransaction();
try
{
switch (version)
{
case 0:
await Migration1(connection, transaction);
version = 1;
break;
case 1:
await Migration2(connection, transaction);
version = 2;
break;
case 2:
await Migration3(connection, transaction);
version = 3;
break;
default:
// ※必要に応じて異常処理を実装してください。
return false;
}
// SQLite のスキーマバージョンを更新する。
await using SqliteCommand setCommand = connection.CreateCommand();
setCommand.Transaction = transaction;
setCommand.CommandText = $"PRAGMA user_version = {version};";
await setCommand.ExecuteNonQueryAsync();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
return true;
}
catch
{
// ※必要に応じて異常処理を実装してください。
return false;
}
}
private static async Task Migration1(SqliteConnection connection, SqliteTransaction transaction)
{
await using SqliteCommand command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = """
CREATE TABLE Users
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL
);
""";
await command.ExecuteNonQueryAsync();
}
private static async Task Migration2(SqliteConnection connection, SqliteTransaction transaction)
{
await using SqliteCommand command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = """
ALTER TABLE Users
ADD COLUMN Email TEXT;
""";
await command.ExecuteNonQueryAsync();
}
private static async Task Migration3(SqliteConnection connection, SqliteTransaction transaction)
{
await using SqliteCommand command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = """
CREATE TABLE Orders
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
UserId INTEGER NOT NULL,
OrderDate TEXT NOT NULL
);
""";
await command.ExecuteNonQueryAsync();
}
}
3. 使用例
アプリケーションのエントリポイントで Migrate() を呼びます。実戦の場合、マイグレーションの前にバックアップを取得した方が良いです。
Program.cs
namespace SQLiteMigrationExample;
/// <summary>
/// アプリケーションのエントリポイントを定義します。
/// </summary>
internal static class Program
{
/// <summary>
/// アプリケーションのエントリポイントです。
/// </summary>
/// <returns>非同期操作を表すタスク。</returns>
[STAThread]
private static async Task Main()
{
// マイグレーションを実行する。
if (!await SQLiteManager.Migrate())
{
// ※必要に応じて異常処理を実装してください。
MessageBox.Show("Failed to migrate the database.");
}
// アプリケーションを実行する。
ApplicationConfiguration.Initialize();
Application.Run(new MainForm());
}
}
4. 参考
おわりに
user_version は SQLite が用意している仕組みなので、別途マイグレーションの管理用テーブルを作らなくてもよくて楽です。SQLite の ALTER TABLE 周りは意図的に機能が限定されてそうなので、スキーマ変更を行う場合は、都度確認した方が良いです。