Xamarin.Forms ローカル データベース

サンプルのダウンロードサンプルのダウンロード

SQLite データベース エンジンを使用すると、 Xamarin.Forms アプリケーションは共有コードでデータ オブジェクトを読み込んで保存できます。 サンプル アプリケーションでは、SQLite データベース テーブルを使用して todo 項目を格納します。 この記事では、共有コードで SQLite.Net を使用して、ローカル データベースに情報を格納および取得する方法について説明します。

iOS および Android 上の Todolist アプリのスクリーンショット

次の手順に従って、SQLite.NET をモバイル アプリに統合します。

  1. NuGet パッケージをインストールします
  2. 定数を構成します
  3. データベース アクセス クラスを作成します
  4. のデータにXamarin.Formsアクセスします。
  5. 高度な構成

SQLite NuGet パッケージをインストールする

NuGet パッケージ マネージャーを使用して sqlite-net-pcl を検索し、共有コード プロジェクトに最新バージョンを追加します。

類似した名前を持つ NuGet パッケージが多数あります。 正しいパッケージには、次の属性があります。

  • ID: sqlite-net-pcl
  • 作成者: SQLite-net
  • 所有者: praeclarum
  • NuGet リンク:sqlite-net-pcl

パッケージ名に関係なく、sqlite-net-pcl NuGet パッケージを .NET Standard プロジェクトでも使用します。

重要

SQLite.NET は、praeclarum/sqlite-net リポジトリからサポートされているサードパーティ製ライブラリです。

アプリ定数を構成する

サンプル プロジェクトには、共通の構成データを提供する Constants.cs ファイルが含まれています。

public static class Constants
{
    public const string DatabaseFilename = "TodoSQLite.db3";

    public const SQLite.SQLiteOpenFlags Flags =
        // open the database in read/write mode
        SQLite.SQLiteOpenFlags.ReadWrite |
        // create the database if it doesn't exist
        SQLite.SQLiteOpenFlags.Create |
        // enable multi-threaded database access
        SQLite.SQLiteOpenFlags.SharedCache;

    public static string DatabasePath
    {
        get
        {
            var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            return Path.Combine(basePath, DatabaseFilename);
        }
    }
}

定数ファイルは、データベース接続の初期化に使用される既定 SQLiteOpenFlag の列挙値を指定します。 列挙型では SQLiteOpenFlag 、次の値がサポートされています。

  • Create: データベース ファイルが存在しない場合、接続によって自動的に作成されます。
  • FullMutex: 接続はシリアル化されたスレッド モードで開かれます。
  • NoMutex: 接続はマルチスレッド モードで開かれます。
  • PrivateCache: 接続が有効になっている場合でも、共有キャッシュには参加しません。
  • ReadWrite: 接続はデータの読み取りと書き込みを行うことができます。
  • SharedCache: 接続が有効になっている場合は、共有キャッシュに参加します。
  • ProtectionComplete: デバイスがロックされている間、ファイルは暗号化され、アクセスできません。
  • ProtectionCompleteUnlessOpen: ファイルは開くまで暗号化されますが、ユーザーがデバイスをロックした場合でもアクセスできます。
  • ProtectionCompleteUntilFirstUserAuthentication: ファイルは、ユーザーがデバイスを起動してロックを解除するまで暗号化されます。
  • ProtectionNone: データベース ファイルが暗号化されていません。

データベースの使用方法に応じて、異なるフラグを指定する必要がある場合があります。 の詳細については SQLiteOpenFlags、「sqlite.org で 新しいデータベース接続を開く 」を参照してください。

データベース アクセス クラスを作成する

データベース ラッパー クラスは、アプリの残りの部分からデータ アクセス層を抽象化します。 このクラスは、クエリ ロジックを一元化し、データベース初期化の管理を簡略化し、アプリの拡大に合わせてデータ操作をリファクタリングまたは拡張しやすくします。 Todo アプリは、この目的のためにクラスを TodoItemDatabase 定義します。

限定的な初期化

では TodoItemDatabase 、カスタム AsyncLazy<T> クラスで表される非同期遅延初期化を使用して、最初にアクセスされるまでデータベースの初期化を遅延させます。

public class TodoItemDatabase
{
    static SQLiteAsyncConnection Database;

    public static readonly AsyncLazy<TodoItemDatabase> Instance = new AsyncLazy<TodoItemDatabase>(async () =>
    {
        var instance = new TodoItemDatabase();
        CreateTableResult result = await Database.CreateTableAsync<TodoItem>();
        return instance;
    });

    public TodoItemDatabase()
    {
        Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
    }

    //...
}

Instanceこのフィールドは、オブジェクトのデータベース テーブルTodoItemを作成するために使用されます (まだ存在しない場合)。また、 はシングルトンとして を返しますTodoItemDatabase。 型AsyncLazy<TodoItemDatabase>の フィールドはInstance、最初に待機されるときに構築されます。 複数のスレッドが同時にフィールドにアクセスしようとすると、すべて 1 つのコンストラクションが使用されます。 その後、構築が完了すると、すべての await 操作が完了します。 さらに、値が使用可能であるため、構築完了後の await 操作は直ちに続行されます。

注意

データベース接続は、アプリの有効期間中に 1 つのデータベース接続が確実に使用されるようにする静的フィールドです。 永続的な静的接続を使用すると、1 つのアプリ セッション中に接続を複数回開いたり閉じたりするよりもパフォーマンスが向上します。

非同期遅延初期化

データベースの初期化を開始し、実行をブロックしないようにし、例外をキャッチする機会を得るために、サンプル アプリケーションでは、 クラスで表される非同期の遅延初期化を AsyncLazy<T> 使用します。

public class AsyncLazy<T>
{
    readonly Lazy<Task<T>> instance;

    public AsyncLazy(Func<T> factory)
    {
        instance = new Lazy<Task<T>>(() => Task.Run(factory));
    }

    public AsyncLazy(Func<Task<T>> factory)
    {
        instance = new Lazy<Task<T>>(() => Task.Run(factory));
    }

    public TaskAwaiter<T> GetAwaiter()
    {
        return instance.Value.GetAwaiter();
    }
}

クラスはAsyncLazy、 型と Task<T> 型をLazy<T>組み合わせて、リソースの初期化を表す遅延初期化タスクを作成します。 コンストラクターに渡されるファクトリ デリゲートは、同期または非同期のいずれかになります。 ファクトリ デリゲートはスレッド プール スレッドで実行され、複数回実行されることはありません (複数のスレッドが同時に開始しようとした場合でも)。 ファクトリ デリゲートが完了すると、遅延初期化された値を使用でき、インスタンスを AsyncLazy<T> 待機しているメソッドは値を受け取ります。 詳しくは、「AsyncLazy」をご覧ください。

データ操作メソッド

クラスには TodoItemDatabase 、作成、読み取り、編集、削除の 4 種類のデータ操作のメソッドが含まれています。 SQLite.NET ライブラリには、SQL ステートメントを記述せずにオブジェクトを格納および取得できる単純なオブジェクト リレーショナル マップ (ORM) が用意されています。

public class TodoItemDatabase
{
    // ...
    public Task<List<TodoItem>> GetItemsAsync()
    {
        return Database.Table<TodoItem>().ToListAsync();
    }

    public Task<List<TodoItem>> GetItemsNotDoneAsync()
    {
        // SQL queries are also possible
        return Database.QueryAsync<TodoItem>("SELECT * FROM [TodoItem] WHERE [Done] = 0");
    }

    public Task<TodoItem> GetItemAsync(int id)
    {
        return Database.Table<TodoItem>().Where(i => i.ID == id).FirstOrDefaultAsync();
    }

    public Task<int> SaveItemAsync(TodoItem item)
    {
        if (item.ID != 0)
        {
            return Database.UpdateAsync(item);
        }
        else
        {
            return Database.InsertAsync(item);
        }
    }

    public Task<int> DeleteItemAsync(TodoItem item)
    {
        return Database.DeleteAsync(item);
    }
}

のデータにアクセスする Xamarin.Forms

クラスは TodoItemDatabase フィールドを Instance 公開します。このフィールドを使用して、 クラスのデータ アクセス操作を TodoItemDatabase 呼び出すことができます。

async void OnSaveClicked(object sender, EventArgs e)
{
    var todoItem = (TodoItem)BindingContext;
    TodoItemDatabase database = await TodoItemDatabase.Instance;
    await database.SaveItemAsync(todoItem);

    // Navigate backwards
    await Navigation.PopAsync();
}

詳細な構成

SQLite は、この記事とサンプル アプリで説明されているよりも多くの機能を備えた堅牢な API を提供します。 次のセクションでは、スケーラビリティに重要な機能について説明します。

詳細については、sqlite.org に関する SQLite ドキュメントを参照してください

先書きログ

既定では、SQLite は従来のロールバック ジャーナルを使用します。 変更されていないデータベース コンテンツのコピーは別のロールバック ファイルに書き込まれ、変更はデータベース ファイルに直接書き込まれます。 COMMIT は、ロールバック ジャーナルが削除されたときに発生します。

Write-Ahead ログ (WAL) は、最初に別の WAL ファイルに変更を書き込みます。 WAL モードでは、COMMIT は特別なレコードであり、WAL ファイルに追加されます。これにより、1 つの WAL ファイルで複数のトランザクションを実行できます。 WAL ファイルは、チェックポイントと呼ばれる特殊な操作でデータベース ファイルにマージ されます

読み取り操作と書き込み操作を同時に行うことができるため、リーダーとライターが互いにブロックし合わないため、ローカル データベースでは WAL の方が高速になります。 ただし、WAL モードでは 、ページ サイズの変更を許可せず、データベースにファイルの関連付けを追加し、 追加のチェックポイント 処理操作を追加します。

SQLite.NET で WAL を有効にするには、 インスタンスで メソッドをEnableWriteAheadLoggingAsyncSQLiteAsyncConnection呼び出します。

await Database.EnableWriteAheadLoggingAsync();

詳細については、「 SQLite Write-Ahead sqlite.org でのログ記録 」を参照してください。

データベースをコピーする

SQLite データベースをコピーする必要がある場合はいくつかあります。

  • データベースはアプリケーションに付属していますが、モバイル デバイス上の書き込み可能ストレージにコピーまたは移動する必要があります。
  • データベースのバックアップまたはコピーを作成する必要があります。
  • データベース ファイルのバージョン管理、移動、または名前変更が必要です。

一般に、データベース ファイルの移動、名前変更、またはコピーは、他のファイルの種類と同じプロセスであり、いくつかの考慮事項があります。

  • データベース ファイルを移動する前に、すべてのデータベース接続を閉じる必要があります。
  • 先書きログを使用する場合、SQLite は共有メモリ アクセス (.shm) ファイルと (先書きログ) (.wal) ファイルを作成します。 これらのファイルにも変更を適用してください。

詳細については、「 でのXamarin.Formsファイル処理」を参照してください。