将 ADO.NET 与 Xamarin.iOS 配合使用

Xamarin 对在 iOS 上可用并通过类似 ADO.NET 的熟悉语法进行公开的 SQLite 数据库提供内置支持。 使用这些 API 需要编写由 SQLite 处理的 SQL 语句,例如 CREATE TABLEINSERTSELECT 语句。

程序集引用

若要通过 ADO.NET 访问 SQLite,必须添加对 iOS 项目的 System.DataMono.Data.Sqlite 引用,如下所示(Visual Studio for Mac 和 Visual Studio 中的示例):

右键单击“引用”>“编辑引用...”,然后单击以选择所需的程序集。

关于 Mono.Data.Sqlite

我们将使用 Mono.Data.Sqlite.SqliteConnection 类创建一个空白数据库文件,然后实例化可用于对数据库执行 SQL 指令的 SqliteCommand 对象。

  1. 创建空白数据库 - 使用有效(即可写)的文件路径调用 CreateFile 方法。 在调用此方法之前,应该检查该文件是否已存在,否则将在旧数据库之上创建新(空白)数据库,并且旧文件中的数据将丢失:

    Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath);

    注意

    应根据本文档前面讨论的规则确定 dbPath 变量。

  2. 创建数据库连接 - 创建 SQLite 数据库文件后,可以创建连接对象来访问数据。 连接是使用 Data Source=file_path 形式的连接字符串构造的,如下所示:

    var connection = new SqliteConnection ("Data Source=" + dbPath);
    connection.Open();
    // do stuff
    connection.Close();
    

    如前所述,切勿在不同的线程之间重用连接。 如果有疑问,请根据需要创建连接并在完成后关闭它;但也要注意这样做的次数要超过要求的次数。

  3. 创建并执行数据库命令 - 建立连接后,我们就可以对其执行任意 SQL 命令。 以下代码演示了正在执行的 CREATE TABLE 语句。

    using (var command = connection.CreateCommand ()) {
        command.CommandText = "CREATE TABLE [Items] ([_id] int, [Symbol] ntext, [Name] ntext);";
        var rowcount = command.ExecuteNonQuery ();
    }
    

直接对数据库执行 SQL 时,应该采取正常的预防措施,不要发出无效请求,例如尝试创建已存在的表。 跟踪数据库的结构,这样就不会导致 SqliteException,例如“SQLite 错误表 [Items] 已存在”。

基本数据访问

在 iOS 上运行时,本文档的 DataAccess_Basic 示例代码如下所示

iOS ADO.NET sample

以下代码演示如何执行简单的 SQLite 操作,并在应用程序的主窗口中以文本形式显示结果。

需要包含这些命名空间:

using System;
using System.IO;
using Mono.Data.Sqlite;

以下代码示例演示整个数据库交互:

  1. 创建数据库文件
  2. 插入一些数据
  3. 查询数据

这些操作通常会出现在代码中的多个位置,例如,可以在应用程序首次启动时创建数据库文件和表,并在应用程序的各个屏幕中执行数据读取和写入。 在以下示例中,已将本示例分组为单个方法:

public static SqliteConnection connection;
public static string DoSomeDataAccess ()
{
    // determine the path for the database file
    string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal),
        "adodemo.db3");

    bool exists = File.Exists (dbPath);

    if (!exists) {
        Console.WriteLine("Creating database");
        // Need to create the database before seeding it with some data
        Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath);
        connection = new SqliteConnection ("Data Source=" + dbPath);

        var commands = new[] {
            "CREATE TABLE [Items] (_id ntext, Symbol ntext);",
            "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('1', 'AAPL')",
            "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('2', 'GOOG')",
            "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('3', 'MSFT')"
        };
        // Open the database connection and create table with data
        connection.Open ();
        foreach (var command in commands) {
            using (var c = connection.CreateCommand ()) {
                c.CommandText = command;
                var rowcount = c.ExecuteNonQuery ();
                Console.WriteLine("\tExecuted " + command);
            }
        }
    } else {
        Console.WriteLine("Database already exists");
        // Open connection to existing database file
        connection = new SqliteConnection ("Data Source=" + dbPath);
        connection.Open ();
    }

    // query the database to prove data was inserted!
    using (var contents = connection.CreateCommand ()) {
        contents.CommandText = "SELECT [_id], [Symbol] from [Items]";
        var r = contents.ExecuteReader ();
        Console.WriteLine("Reading data");
        while (r.Read ())
            Console.WriteLine("\tKey={0}; Value={1}",
                              r ["_id"].ToString (),
                              r ["Symbol"].ToString ());
    }
    connection.Close ();
}

更复杂的查询

由于 SQLite 允许对数据运行任意 SQL 命令,因此你可以执行偏好的任何 CREATE、INSERT、UPDATE、DELETE 或 SELECT 语句。 可以在 Sqlite 网站上了解 SQLite 支持的 SQL 命令。 SQL 语句使用 SqliteCommand 对象上的三个方法之一来运行:

  • ExecuteNonQuery – 通常用于表创建或数据插入。 某些操作的返回值是受影响的行数,否则为 -1。
  • ExecuteReader – 应将行集合作为 SqlDataReader 返回时使用。
  • ExecuteScalar – 检索单个值(例如聚合)

EXECUTENONQUERY

INSERT、UPDATE 和 DELETE 语句将返回受影响的行数。 所有其他 SQL 语句将返回 -1。

using (var c = connection.CreateCommand ()) {
    c.CommandText = "INSERT INTO [Items] ([_id], [Symbol]) VALUES ('1', 'APPL')";
    var rowcount = c.ExecuteNonQuery (); // rowcount will be 1
}

EXECUTEREADER

以下方法显示 SELECT 语句中的 WHERE 子句。 由于代码正在编写完整的 SQL 语句,因此它必须注意转义保留字符,例如字符串两侧的引号 (')。

public static string MoreComplexQuery ()
{
    var output = "";
    output += "\nComplex query example: ";
    string dbPath = Path.Combine (
        Environment.GetFolderPath (Environment.SpecialFolder.Personal), "ormdemo.db3");

    connection = new SqliteConnection ("Data Source=" + dbPath);
    connection.Open ();
    using (var contents = connection.CreateCommand ()) {
        contents.CommandText = "SELECT * FROM [Items] WHERE Symbol = 'MSFT'";
        var r = contents.ExecuteReader ();
        output += "\nReading data";
        while (r.Read ())
            output += String.Format ("\n\tKey={0}; Value={1}",
                    r ["_id"].ToString (),
                    r ["Symbol"].ToString ());
    }
    connection.Close ();

    return output;
}

ExecuteReader 方法返回 SqliteDataReader 对象。 除了示例中显示的 Read 方法之外,其他有用的属性还包括:

  • RowsAffected – 受查询影响的行数
  • HasRows – 是否返回了任何行

EXECUTESCALAR

将此项用于返回单个值(例如聚合)的 SELECT 语句。

using (var contents = connection.CreateCommand ()) {
    contents.CommandText = "SELECT COUNT(*) FROM [Items] WHERE Symbol <> 'MSFT'";
    var i = contents.ExecuteScalar ();
}

ExecuteScalar 方法的返回类型是 object – 应该根据数据库查询来强制转换结果。 结果可以是来自 COUNT 查询的整数或来自单列 SELECT 查询的字符串。 请注意,这与返回读取器对象或受影响行数的其他 Execute 方法不同。

Microsoft.Data.Sqlite

还有另一个可以从 NuGet 安装的库 Microsoft.Data.Sqlite,其功能等效于 Mono.Data.Sqlite,并且允许相同类型的查询。

我们提供了两个库之间的比较以及一些特定于 Xamarin 的详细信息。 对于 Xamarin.iOS 应用而言,最重要的是必须包含初始化调用:

// required for Xamarin.iOS
SQLitePCL.Batteries_V2.Init();