结合使用 SQLite.NET 和 Android

Xamarin 推荐的 SQLite.NET 库是一个非常基本的 ORM,可让你在 iOS 设备上的本地 SQLite 数据库中轻松存储和检索对象。 ORM 是对象关系映射的英文缩写,它是一个 API,可让你在数据库中保存和检索“对象”,而无需编写 SQL 语句。

若要将 SQLite.NET 库包含在 Xamarin 应用中,请将以下 NuGet 包添加到项目:

  • 包名称:sqlite-net-pcl
  • 作者:Frank A. Krueger
  • ID:sqlite net pcl
  • URL:nuget.org/packages/sqlite-net-pcl

SQLite.NET NuGet package

有许多不同的 SQLite 包可供使用 - 请务必选择正确的一个(它可能不是搜索中排名最靠前的结果)。

重要

SQLite.NET 是 praeclarum/sqlite-net 存储库支持的第三方库。

获得可用的 SQLite.NET 库后,请按照以下三个步骤使用它来访问数据库:

  1. 添加 using 语句 - 将以下语句添加到需要数据访问的 C# 文件中

    using SQLite;
    
  2. 创建空白数据库 - 可以通过将文件路径传递给 SQLiteConnection 类构造函数来创建数据库引用。 无需检查文件是否已存在 - 如果需要,它将自动创建,否则将打开现有的数据库文件。 应根据本文档前面讨论的规则确定 dbPath 变量:

    var db = new SQLiteConnection (dbPath);
    
  3. 保存数据 - 创建 SQLiteConnection 对象后,将通过调用其方法(例如 CreateTable 和 Insert)来执行数据库命令,如下所示

    db.CreateTable<Stock> ();
    db.Insert (newStock); // after creating the newStock object
    
  4. 检索数据 - 要检索对象(或对象列表),请使用以下语法

    var stock = db.Get<Stock>(5); // primary key id of 5
    var stockList = db.Table<Stock>();
    

基本数据访问示例

在 Android 上运行时,本文档的 DataAccess_Basic 示例代码如下所示。 该代码演示如何执行简单的 SQLite.NET 操作,并在应用程序的主窗口中以文本形式显示结果。

Android

Android SQLite.NET sample

以下代码示例演示了使用 SQLite.NET 库封装基础数据库访问的整个数据库交互。 该示例演示了以下操作:

  1. 创建数据库文件

  2. 通过创建对象来插入一些数据,然后保存它们

  3. 查询数据

需要包含以下命名空间:

using SQLite; // from the github SQLite.cs class

最后一个要求将 SQLite 添加到项目中。 请注意,SQLite 数据库表是通过向类(Stock 类)添加特性而不是通过 CREATE TABLE 命令来定义的。

[Table("Items")]
public class Stock {
    [PrimaryKey, AutoIncrement, Column("_id")]
    public int Id { get; set; }
    [MaxLength(8)]
    public string Symbol { get; set; }
}
public static void DoSomeDataAccess () {
       Console.WriteLine ("Creating database, if it doesn't already exist");
   string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "ormdemo.db3");
   var db = new SQLiteConnection (dbPath);
   db.CreateTable<Stock> ();
   if (db.Table<Stock> ().Count() == 0) {
        // only insert the data if it doesn't already exist
        var newStock = new Stock ();
        newStock.Symbol = "AAPL";
        db.Insert (newStock);
        newStock = new Stock ();
        newStock.Symbol = "GOOG";
        db.Insert (newStock);
        newStock = new Stock ();
        newStock.Symbol = "MSFT";
        db.Insert (newStock);
    }
    Console.WriteLine("Reading data");
    var table = db.Table<Stock> ();
    foreach (var s in table) {
        Console.WriteLine (s.Id + " " + s.Symbol);
    }
}

使用 [Table] 特性而不指定表名称参数将导致基础数据库表与类具有相同的名称(在本例中为“Stock”)。 如果直接针对数据库编写 SQL 查询而不是使用 ORM 数据访问方法,则实际的表名称非常重要。 同样,[Column("_id")] 特性是可选的,如果不存在,将向表中添加与类中的属性同名的列。

SQLite 特性

可应用于类以控制它们在基础数据库中的存储方式的常见特性包括:

  • [PrimaryKey] - 可以将此特性应用于整数属性,以强制其成为基础表的主键。 不支持复合主键。

  • [AutoIncrement] - 此特性将导致整数属性的值针对插入数据库的每个新对象自动递增

  • [Column(name)] - name 参数设置基础数据库列的名称。

  • [Table(name)] - 将类标记为能够存储在具有指定名称的基础 SQLite 表中

  • [MaxLength(value)] - 尝试执行数据库插入时限制文本属性的长度。 使用代码应在插入对象之前验证此特性,因为仅在尝试数据库插入或更新操作时才会“检查”此特性。

  • [Ignore] - 导致 SQLite.NET 忽略此属性。 这对于具有无法存储在数据库中的类型的属性,或者对无法由 SQLite 自动解析的集合建模的属性特别有用。

  • [Unique] - 确保基础数据库列中的值是唯一的

其中大多数特性都是可选的。 始终应该指定整数主键,以便可以对数据高效执行选择和删除查询。

更复杂的查询

SQLiteConnection 中的以下方法可用于执行其他数据操作:

  • Insert - 将新对象添加到数据库中

  • Get<T> - 尝试使用主键检索对象

  • Table<T> - 返回表中的所有对象。

  • Delete - 使用主键删除对象

  • Query<T> - 执行返回多行(作为对象)的 SQL 查询。

  • Execute - 当你不希望从 SQL(例如 INSERT、UPDATE 和 DELETE 指令)返回行时,请使用此方法(而不是 Query)。

按主键获取对象

SQLite.Net 提供了 Get 方法来根据主键检索单个对象。

var existingItem = db.Get<Stock>(3);

使用 Linq 选择对象

返回集合的方法支持 IEnumerable<T>,因此可以使用 Linq 对表的内容进行查询或排序。 以下代码演示了使用 Linq 筛选掉以字母“A”开头的所有条目的示例:

var apple = from s in db.Table<Stock>()
    where s.Symbol.StartsWith ("A")
    select s;
Console.WriteLine ("-> " + apple.FirstOrDefault ().Symbol);

使用 SQL 选择对象

尽管 SQLite.Net 可以提供对数据的基于对象的访问,但有时你可能需要执行比 Linq 允许的查询更复杂的查询(或者可能需要更快的性能)。 可以将 SQL 命令与 Query 方法结合使用,如下所示:

var stocksStartingWithA = db.Query<Stock>("SELECT * FROM Items WHERE Symbol = ?", "A");
foreach (var s in stocksStartingWithA) {
    Console.WriteLine ("a " + s.Symbol);
}

注意

直接编写 SQL 语句时,将会创建对数据库中表和列的名称的依赖关系,这些名称是从类及其特性生成的。 如果在代码中更改这些名称,请务必记得更新所有手动编写的 SQL 语句。

删除对象

主键用于删除行,如下所示:

var rowcount = db.Delete<Stock>(someStock.Id); // Id is the primary key

可以检查 rowcount 以确认有多少行受到影响(在本例中为已删除)。

结合多个线程使用 SQLite.NET

SQLite 支持三种不同的线程模式:单线程、多线程和序列化。 如果你希望不受任何限制地从多个线程访问数据库,可以将 SQLite 配置为使用序列化线程模式。 在应用程序中尽早设置此模式非常重要(例如,在 OnCreate 方法的开头)。

若要更改线程模式,请调用 SqliteConnection.SetConfig。 例如,此代码行将 SQLite 配置为“序列化”模式

using using Mono.Data.Sqlite;
...
SqliteConnection.SetConfig(SQLiteConfig.Serialized);

Android 版 SQLite 有一个限制,需要执行更多步骤。 如果对 SqliteConnection.SetConfig 的调用产生一个 SQLite 异常(如 library used incorrectly),则必须使用以下解决方法:

  1. 链接到本机 libsqlite.so 库,使 sqlite3_shutdownsqlite3_initialize API 可供应用使用

    [DllImport("libsqlite.so")]
    internal static extern int sqlite3_shutdown();
    
    [DllImport("libsqlite.so")]
    internal static extern int sqlite3_initialize();
    
  2. OnCreate 方法的开头,添加此代码以关闭 SQLite,将其配置为“已序列化”模式,并重新初始化 SQLite

    using using Mono.Data.Sqlite;
    ...
    sqlite3_shutdown();
    SqliteConnection.SetConfig(SQLiteConfig.Serialized);
    sqlite3_initialize();
    

此解决方法也适用于 Mono.Data.Sqlite 库。 有关 SQLite 和多线程的详细信息,请参阅 SQLite 和多个线程