mcpField
M
maui-sqlite-database

maui-sqlite-database

github.com/davidortinauデータ・データベース17650 回インストール16 週間前に更新
リポジトリ

maui-sqlite-database について

.NET MAUI アプリで SQLite を扱うための実践的なスキルです。接続のライフサイクルやパス、並行処理、性能に関する注意点をまとめています。

主な機能

  1. 01クロスプラットフォームのDBパス案内
  2. 02シングルトン接続と遅延初期化
  3. 03WALモードと並行処理の指針
  4. 04安全なデータベースファイル操作
  5. 05クエリと一括書き込みの最適化

活用シーン

  1. 01MAUI各プラットフォームのDBパス選定
  2. 02重複接続による破損の防止
  3. 03一括書き込みと大規模テーブルのクエリ計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davidortinau/maui-skills --skill maui-sqlite-database

インストール後、Claude Code で/maui-sqlite-databaseを入力して読み込まれたか確認します。

同梱ファイルreferences/sqlite-database-api.md

詳細

SQLite データベース — 落とし穴とベストプラクティス

サービスの実装全体、定数、データモデルのテンプレート、一般的なパターンについては、references/sqlite-database-api.md を参照してください。

⚠️ パッケージ選択の落とし穴


<PackageReference Include="Microsoft.Data.Sqlite" />
<PackageReference Include="sqlite-net" />
<PackageReference Include="SQLitePCL.raw" />


<PackageReference Include="sqlite-net-pcl" Version="1.9.*" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.*" />

よくある間違い

❌ データベースのパスに Environment.GetFolderPath を使用する
// ❌ Not cross-platform safe — fails on some MAUI targets
var path = Path.Combine(Environment.GetFolderPath(
    Environment.SpecialFolder.LocalApplicationData), "app.db3");

// ✅ Use FileSystem.AppDataDirectory for all MAUI platforms
var path = Path.Combine(FileSystem.AppDataDirectory, "app.db3");
❌ 複数の SQLiteAsyncConnection インスタンスを使用する

SQLiteAsyncConnection は、同じファイルを指す複数のインスタンス間ではスレッドセーフではありません。DI のシングルトンを通じて、単一のインスタンスを使用してください。

// ❌ Creating new connections per request
public async Task<List<Item>> GetItems()
{
    var db = new SQLiteAsyncConnection(Constants.DatabasePath);
    return await db.Table<Item>().ToListAsync();
}

// ✅ Lazy singleton — one connection, created once
private SQLiteAsyncConnection? _database;
private async Task<SQLiteAsyncConnection> GetDatabaseAsync()
{
    if (_database is not null) return _database;
    _database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    await _database.ExecuteAsync("PRAGMA journal_mode=WAL;");
    await _database.CreateTableAsync<TodoItem>();
    return _database;
}
❌ WAL モードを有効にし忘れる

WAL を有効にしないと、読み取り側が書き込み側をブロックします。初期化時に必ず有効にしてください。

await _database.ExecuteAsync("PRAGMA journal_mode=WAL;");
❌ データベースを開いたままファイル操作を行う
// ❌ Moving/deleting while connection is open — data corruption
File.Delete(Constants.DatabasePath);

// ✅ Always close first
await databaseService.CloseConnectionAsync();
if (File.Exists(Constants.DatabasePath))
    File.Delete(Constants.DatabasePath);

プラットフォーム固有の注意点

プラットフォーム注意点
iOSFileSystem.AppDataDirectory は iCloud のバックアップ対象です。データベースを iCloud バックアップの対象外にするには、FileSystem.CacheDirectory を使用してください
すべて同じファイルに対して複数の SQLiteAsyncConnection インスタンスを使用すると、データが破損します
すべてWAL が無効だと、読み取り側が書き込み側をブロックし、同時実行時のパフォーマンスが低下します
すべてデータベースを開いたままファイル操作を行うと、データが破損します

判断の指針

質問推奨事項
DI のライフタイムは?シングルトン — 接続は 1 つにし、同時読み取りは WAL で処理します
WAL モードを有効にする?必ず有効にする — モバイルでは無効にする理由はありません
データベースのパスは?FileSystem.AppDataDirectory — Environment.GetFolderPath は使用しないでください
保存パターンは?Id != 0 を確認し、真なら Update、そうでなければ Insert
複数のテーブルが必要?遅延初期化に CreateTableAsync<T>() の呼び出しをすべて追加します
エクスポート/バックアップが必要?先に接続を閉じてから、File.Copy を実行します

パフォーマンスに関するヒント

  1. 一括書き込みにはトランザクションを使用する — 個別の挿入は遅いため、RunInTransactionAsync で囲みます
  2. 頻繁にクエリする列に [Indexed] を追加する — 特に外部キーに追加します
  3. WAL モードにより、読み取りと書き込みの競合を解消できます
  4. 大きなテーブルでは ToListAsync() を避ける — Where() によるフィルタリングとページネーションを使用します
  5. 複雑なクエリには生の SQL を使用する — 結合では、QueryAsync<T> のほうが LINQ の連結より高速です

チェックリスト

  • sqlite-net-pcl と SQLitePCLRaw.bundle_green をインストールする(Microsoft.Data.Sqlite ではありません)
  • データベースのパスに FileSystem.AppDataDirectory を使用する
  • モデルに [PrimaryKey, AutoIncrement] を指定する
  • 遅延非同期初期化パターンを使う DatabaseService を 1 つだけ用意する
  • PRAGMA journal_mode=WAL で WAL を有効にする
  • DI で DatabaseService をシングルトンとして登録する
  • ファイルの移動/コピー/削除を行う前に接続を閉じる
  • iOS:必要に応じて、データベースを iCloud バックアップの対象外にする

コメント