将 ADO.NET 与 Android 配合使用

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

程序集引用

若要通过 ADO.NET 访问 SQLite,必须添加对 Android 项目的 System.DataMono.Data.Sqlite 引用,如下所示:

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

关于 Mono.Data.Sqlite

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

创建空白数据库 - 使用有效(即可写)的文件路径调用 CreateFile 方法。 在调用此方法之前,应该检查文件是否已存在,否则将在旧数据库之上创建新(空白)数据库,并且旧文件中的数据将丢失。 Mono.Data.Sqlite.SqliteConnection.CreateFile (dbPath); 应根据本文档前面讨论的规则确定 dbPath 变量。

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

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

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

创建并执行数据库命令 - 建立连接后,我们就可以对其执行任意 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] 已存在”

基本数据访问

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

Android 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 命令,因此你可以执行偏好的任何 CREATEINSERTUPDATEDELETESELECT 语句。 可以在 SQLite 网站上了解 SQLite 支持的 SQL 命令。 SQL 语句使用 SqliteCommand 对象上的三个方法之一来运行:

  • ExecuteNonQuery - 通常用于表创建或数据插入。 某些操作的返回值是受影响的行数,否则为 -1。

  • ExecuteReader - 在应将行集合作为 SqlDataReader 返回时使用。

  • ExecuteScalar - 检索单个值(例如聚合)。

EXECUTENONQUERY

INSERTUPDATEDELETE 语句将返回受影响的行数。 所有其他 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 方法不同。