Xamarin.Forms 本機資料庫

Download Sample 下載範例

SQLite 資料庫引擎可讓 Xamarin.Forms 應用程式在共用程式碼中載入和儲存資料物件。 範例應用程式會使用 SQLite 資料庫資料表來儲存 todo 專案。 本文說明如何使用共用程序代碼中的 SQLite.Net,在本機資料庫中儲存和擷取資訊。

Screenshots of the Todolist app on iOS and Android

請遵循下列步驟,將 SQLite.NET 整合到行動裝置應用程式中:

  1. 安裝 NuGet 套件
  2. 設定常數
  3. 建立數據庫存取類別
  4. 存取 中的數據 Xamarin.Forms
  5. 進階設定。

安裝 SQLite NuGet 套件

使用 NuGet 套件管理員來搜尋 sqlite-net-pcl ,並將最新版本新增至共用程式碼專案。

有許多名稱類似的 NuGet 套件。 正確的套件有下列屬性:

  • 識別碼: sqlite-net-pcl
  • 作者: SQLite-net
  • 擁有者:praeclarum
  • NuGet 連結:sqlite-net-pcl

不論套件名稱為何,請使用 sqlite-net-pcl NuGet 套件,即使在專案中也一樣。

重要

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 。 類型InstanceAsyncLazy<TodoItemDatabase>為 的欄位會在第一次等候時建構。 如果多個線程同時嘗試存取字段,它們都會使用單一建構。 然後,當建構完成時,所有作業都會 await 完成。 此外,建構完成後的任何 await 作業都會立即繼續,因為有可用的值。

注意

資料庫連接是靜態欄位,可確保單一資料庫連接用於應用程式生命週期。 使用持續性靜態連線可提供比在單一應用程式會話期間多次開啟和關閉連線更好的效能。

異步延遲初始化

為了啟動資料庫初始化,避免封鎖執行,並有機會攔截例外狀況,範例應用程式會使用類別所代表的 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();
    }
}

類別 AsyncLazyLazy<T> 結合 和 Task<T> 型別,以建立延遲初始化的工作,此工作代表資源的初始化。 傳遞至建構函式的處理站委派可以是同步或異步的。 處理站委派會在線程集區線程上執行,而且不會多次執行(即使多個線程嘗試同時啟動它們也一次)。 當處理站委派完成時,可以使用延遲初始化的值,而等候 AsyncLazy<T> 實例的任何方法都會收到值。 如需詳細資訊,請參閱 AsyncLazy

數據操作方法

類別 TodoItemDatabase 包含四種數據操作類型的方法:建立、讀取、編輯和刪除。 SQLite.NET 連結庫提供簡單的物件關係型對應 (ORM),可讓您儲存和擷取物件,而不需要撰寫 SQL 語句。

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

類別 TodoItemDatabaseInstance 公開欄位,您可以透過此欄位叫用 類別中的數據 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。

預先寫入記錄 (WAL) 會先將變更寫入個別的 WAL 檔案。 在 WAL 模式中,COMMIT 是附加至 WAL 檔案的特殊記錄,可讓單一 WAL 檔案中發生多個交易。 WAL 檔案會在稱為 檢查點的特殊作業中,合併回資料庫檔案。

WAL 對於本機資料庫來說可能更快,因為讀取器和寫入器不會彼此封鎖,因此允許讀取和寫入作業同時運作。 不過,WAL 模式不允許變更 頁面大小、將其他檔案關聯新增至資料庫,以及新增額外的 檢查點 作業。

若要在 SQLite.NET 中啟用 WAL,請在 實體上SQLiteAsyncConnection呼叫 EnableWriteAheadLoggingAsync 方法:

await Database.EnableWriteAheadLoggingAsync();

如需詳細資訊,請參閱 SQLite 預先寫入記錄 sqlite.org。

複製資料庫

在某些情況下,可能需要複製 SQLite 資料庫:

  • 資料庫隨附於您的應用程式,但必須複製或移至行動裝置上的可寫入記憶體。
  • 您必須建立資料庫的備份或複本。
  • 您需要版本、移動或重新命名資料庫檔案。

一般而言,移動、重新命名或複製資料庫檔案的程式與任何其他檔類型的程式相同,但有一些其他考慮:

  • 嘗試移動資料庫檔案之前,應該先關閉所有資料庫連接。
  • 如果您使用 Write-Ahead 記錄,SQLite 會建立共用記憶體存取 (.shm) 檔案和 [寫入預先記錄] (.wal) 檔案。 請確定您也對這些檔案套用任何變更。

如需詳細資訊,請參閱 中的 Xamarin.Forms檔案處理。