SQLite 数据库引擎允许 Xamarin.Forms 应用程序在共享代码中加载和保存数据对象。 示例应用程序使用 SQLite 数据库表来存储 todo 项。 本文介绍如何使用共享代码中的 SQLite.Net 在本地数据库中存储和检索信息。
按照以下步骤将 SQLite.NET 集成到移动应用中:
安装 NuGet 包
。
配置常量
。
创建数据库访问类
。
访问 中的数据 Xamarin.Forms
。
高级配置
。
安装 SQLite NuGet 包
使用 NuGet 包管理器搜索
sqlite-net-pcl
并将最新版本添加到共享代码项目。
许多 NuGet 包都有着类似的名称。 正确的包具有以下属性:
ID:
sqlite net pcl
作者:SQLite-net
所有者:praeclarum
NuGet 链接:
sqlite-net-pcl
不管包名称,即便在 .NET Standard 项目中也使用 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
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
作为单一实例的 。 类型 Instance
为 的 AsyncLazy<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();
类AsyncLazy
将 和 Task<T>
类型组合Lazy<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);
return Database.InsertAsync(item);
public Task<int> DeleteItemAsync(TodoItem item)
return Database.DeleteAsync(item);
类 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 文件的特殊记录,允许在单个 WAL 文件中发生多个事务。 WAL 文件在称为 检查点的特殊操作中合并回数据库文件。
对于本地数据库来说,WAL 速度更快,因为读取器和写入器不会相互阻止,因此允许读取和写入操作并发。 但是,WAL 模式不允许更改 页面大小、向数据库添加其他文件关联以及添加额外的 检查点 操作。
若要在 SQLite.NET 中启用 WAL,请在 实例上SQLiteAsyncConnection
调用 EnableWriteAheadLoggingAsync
方法:
await Database.EnableWriteAheadLoggingAsync();
有关详细信息,请参阅 SQLite Write-Ahead sqlite.org 上的日志记录 。
复制数据库
在某些情况下,可能需要复制 SQLite 数据库:
数据库已随应用程序一起提供,但必须复制或移动到移动设备上的可写存储。
需要创建数据库的备份或副本。
需要对数据库文件进行版本控制、移动或重命名。
通常,移动、重命名或复制数据库文件的过程与任何其他文件类型相同,但需注意一些其他事项:
在尝试移动数据库文件之前,应关闭所有数据库连接。
如果使用 预写日志记录,SQLite 将创建共享内存访问 (.shm) 文件和 (预写日志) (.wal) 文件。 确保也对这些文件应用任何更改。
有关详细信息,请参阅 中的Xamarin.Forms文件处理。
Todo 示例应用程序
SQLite.NET NuGet 包
SQLite 文档
将 SQLite 与 Android 配合使用
将 SQLite 与 iOS 配合使用
AsyncLazy