拥有关系数据时,在 SQLite 中存储数据十分有用。 例如,假设正在生成用于管理图书馆的应用程序。 图书馆中的每本书都有一位或多位作者,而一名作者可以写多本书。 可在 SQLite 数据库中轻松地为这种关系建模。
本单元将介绍如何通过 SQLite.NET 在 Xamarin 应用程序中使用 SQLite。
SQLite 是轻型跨平台本地数据库,其已成为移动应用程序的行业标准。 SQLite 不在服务器上运行,并存储在设备文件系统上的单一磁盘文件中。 所有读写操作都直接针对 SQLite 磁盘文件运行。
SQLite 本机库默认内置于 Android 和 iOS 中;但其引擎只支持 C/C++ API。 对于想要 SQLite 和 .NET 通过某种方式进行交互的 .NET 开发人员来说,此方案并不理想。
备注:如果想要下载 SQLite.NET,可在此处找到它:
https://www.nuget.org/packages/sqlite-net
C++学习资料→CC++入门到高级资料
本机 SQLite 引擎有多个 C# 包装器可供 .NET 开发人员使用。 许多 Xamarin 开发人员使用名为 SQLite.NET 的常用 C# 包装器。
SQLite.NET 是对象关系映射器。 它通过让我们能够将在项目中定义的模型用作架构,帮助简化定义数据库架构的过程。 以下面的代码片段为例:
class User{ public int Id { get; set; } public string Username { get; set; } ...}
借助对象关系映射器,可使用此初始“User” 类,并让其自动创建包含“Id”和“Username”列的名为“User”的数据库表。
SQLite.NET 作为 NuGet 包提供。 将它添加到 Xamarin.Forms 的每个项目中。
SQLite.Net 通过“SQLiteConnection”对象建立与 SQLite 数据库的连接。 实例化此对象时,必须传入数据库文件的文件名。 然后,它将打开文件(如果存在文件)或创建文件(如果不存在文件)。
下面是其用法示例:
string filename = ...SQLiteConnection conn = new SQLiteConnection(filename);
请注意,filename 需要指向应用沙盒中的位置。
回想一下,SQLite.NET 是对象关系映射器,这意味着可以通过 C# 类生成数据库架构。 返回 User 类的上一个示例。
class User{ public int Id { get; set; } public string Username { get; set; } ...}
SQLite.NET 可通过此 C# 类生成数据库架构,但许多属性可供添加到类中,用于对架构进行修改。
以下是可用属性的一些示例:
回到“User”类,下面是使用所有这些属性的更新版本:
[Table("user")]public class User{ // PrimaryKey is typically numeric [PrimaryKey, AutoIncrement, Column("_id")] public int Id { get; set; } [MaxLength(250), Unique] public string Username { get; set; } ...}
定义要用作数据库架构的 C# 类后,需要让 SQLite.NET 创建表。 为此,请在“SQLiteConnection”类上使用“CreateTable”方法。 下面是一个示例:
SQLiteConnection conn = new SQLiteConnection(filename);conn.CreateTable<User>();
如果调用“CreateTable”方法且表已存在于数据库中,则它将检查架构类,查看是否有任何更改。 如果有任何更改,将执行更新操作,尝试更新数据库架构。
创建表后,即可开始与它进行交互。 首先可插入一些数据。 为此,请在 SQLiteConnection 实例上使用 Insert 方法。 例如,如果想要将新的“User”插入数据库,则代码将类似于以下示例:
public int AddNewUser(User user){ int result = conn.Insert(user); return result;}
“Insert”方法返回“int”,其表示插入表中的行数。 在本例中,该数字为 1。
插入数据后,通常需要从表中检索数据。 借助 SQLite.NET,可使用“Table”方法轻松地在表中检索所有行。 以下是如何使用它的示例:
public List<User> GetAllUsers(){ List<User> users = conn.Table<User>().ToList(); return users;}
“Table”方法返回“TableQueryT”。 若要获取“List”,请使用“ToList”方法。
虽然可使用“Table”方法在表中检索所有行,但并不总是想要这样做。 有时,想要仅返回行的子集或运行更复杂的查询。 对于这些任务,请将 LINQ 与 SQLite.NET 配合使用。
SQLite.NET 支持许多常见的 LINQ 查询,其中包括:
通过这些方法,你可以使用扩展方法语法或 LINQ C# 语法。 例如,下面是可供使用 LINQ C# 语法按用户名获取用户的代码片段:
public User GetByUsername(string username){ var user = from u in conn.Table<Person>() where u.Username == username select u; return user.FirstOrDefault();}
首先,创建用于定义数据库架构的模型类。
namespace People.Models{ public class Person { }}
3.添加名为“ID”的 int 属性。
4.添加名为 Name 的字符串属性。
namespace People.Models{ public class Person { public int Id { get; set; } public string Name { get; set; } }}
现在已创建了模型,接下来可添加一些属性用于帮助 SQLite.NET 将类映射到表中。
using SQLite;namespace People.Models{ [Table("people")] public class Person { [PrimaryKey, AutoIncrement] public int Id { get; set; } [MaxLength(250), Unique] public string Name { get; set; } }}
5.生成应用程序,确保其能正常编译。
接下来,需要为跨平台项目添加一些预生成的代码。 已提供此代码。 您可以在练习存储库的克隆或下载副本的文件夹中找到它。
现在,将在“SQLiteConnection”上创建一个实例,然后创建一个“Person”表。
private SQLiteConnection conn;...public PersonRepository(string dbPath){ conn = new SQLiteConnection(dbPath); conn.CreateTable<Person>();}
创建“Person”表后,即可开始插入数据。 实现“AddNewPerson”方法,使用户能够插入新人员。
public void AddNewPerson(string name){ int result = 0; try { //basic validation to ensure a name was entered if (string.IsNullOrEmpty(name)) throw new Exception("Valid name required"); result = conn.Insert(new Person { Name = name }); ... } ...}
此时,可以向“Person”表添加新行。 尝试使用“Table”方法读取它们。
public List<Person> GetAllPeople(){ try { return conn.Table<Person>().ToList(); } catch (Exception ex) { StatusMessage = string.Format("Failed to retrieve data. {0}", ex.Message); } return new List<Person>();}
最后一步是添加 UI,以调用在上一步中实现的方法。 使用练习所用的“Assets”文件夹中包含的预生成的 UI。
生成解决方案,并运行应用程序。 通过在文本框中键入名称并选择“添加人员”,将人员添加到数据库中。
之后,可选择“获取所有人员”,以从数据库中提取名称并将在列表中显示它们。 此外,尝试重启应用程序,查看数据在应用程序启动期间是否还在。
如果以同步方式对数据库运行查询,可能会导致性能问题。 SQLite.NET 具有一个异步 API,可用于使应用程序始终保持响应。
本单元将介绍如何使用 SQLite.NET 的异步 API 来确保应用程序保持高度响应。
到目前为止,所做的一切均已在 UI 线程上执行。 但是,若要生成响应速度极快的移动应用程序,需要以略有不同的方式执行操作。 如果在 UI 线程上运行数据库操作,则可能会导致 UI 在操作需要很长时间才能完成时冻结。
为了解决此问题,SQLite.NET 通过“SQLiteAsyncConnection”类包含了一个异步 API。 例如,若要异步创建表,可使用
var conn = new SQLiteAsyncConnection(dbPath);await conn.CreateTableAsync<User>();
SQLite.NET 不是线程安全的。 这意味着如果多个线程使用同一个连接,你可能会遇到问题。 因此,最好有一个负责管理“SQLite”连接的存储库类。
“SQLiteAsyncConnection”公开与同步对应类相同的操作。 但是,操作均基于任务,以便在后台使用。
下面列出了一些常见的异步操作:
以下是使用“ToListAsync”方法异步检索记录的示例:
SQLiteAsyncConnection conn;ObservableCollection<User> userList; // Bound to UI...public async Task AddAllUsersAsync(){ List<User> users = await conn.Table<User>().ToListAsync(); // Must be on UI thread here! foreach (var u in users) userList.Add(u);}
使用“ToListAsync”方法从数据库异步获取所有用户。 如果使用此方法,即使数据库中有大量用户,UI 也能保持响应。
本单元将把 Person 存储库应用程序从同步 SQLite.NET API 转换为异步版本。 这样一来,无论对数据库执行多少次查询,应用程序将始终能够保持响应。
首先将“PersonRepository”更改为使用“SQLiteConnection”的异步版本。 现在,可以与数据库进行异步交互。
private SQLiteAsyncConnection conn;public PersonRepository(string dbPath){ conn = new SQLiteAsyncConnection(dbPath); conn.CreateTableAsync<Person>().Wait();}
现在使用的是“SQLiteAsyncConnection”,可与数据库进行异步交互。 接下来了解如何以异步方式插入新项。
修改“AddNewPerson”方法,以通过异步插入方式插入新的“Person”。
using System.Threading.Tasks;...public async Task AddNewPersonAsync(string name){ int result = 0; try { //basic validation to ensure a name was entered if (string.IsNullOrEmpty(name)) throw new Exception("Valid name required"); // TODO: insert a new person into the Person table result = await conn.InsertAsync(new Person { Name = name }); StatusMessage = string.Format("{0} record(s) added [Name: 事件处理)", result, name); } catch (Exception ex) { StatusMessage = string.Format("Failed to add {0}. Error: 事件处理", name, ex.Message); }}
最后,从数据库中异步检索“People”。
public async Task<List<Person>> GetAllPeopleAsync(){ try { return await conn.Table<Person>().ToListAsync(); } catch (Exception ex) { StatusMessage = string.Format("Failed to retrieve data. {0}", ex.Message); } return new List<Person>();}
2.在“MainPage.xaml.cs”文件中,修改两个“Button.Click”事件处理程序,以使用“PersonRepository”类中的异步方法。 使用 async 和 await 关键字。
3.运行该程序,验证它是否仍能正常运行。
在移动设备本地存储数据对于提高性能十分有用。 可以在本地存储重要数据并快速检索,而不是通过不断地调用远程服务器来获取数据。
可用的存储选项因拥有的数据类型而异。 处理本质上为关系数据的数据时,数据库是最佳选项。
可使用 SQLite 在 Xamarin 应用程序中创建本地数据库。 SQLite.NET 是 SQLite 的 C# 包装器。 它公开异步 API,以帮助确保应用程序的 UI 始终保持响应。