将 ADO.NET 与 Xamarin.iOS 配合使用
Xamarin 对在 iOS 上可用并通过类似 ADO.NET 的熟悉语法进行公开的 SQLite 数据库提供内置支持。 使用这些 API 需要编写由 SQLite 处理的 SQL 语句,例如 CREATE TABLE
、INSERT
和 SELECT
语句。
程序集引用
若要通过 ADO.NET 访问 SQLite,必须添加对 iOS 项目的 System.Data
和 Mono.Data.Sqlite
引用,如下所示(Visual Studio for Mac 和 Visual Studio 中的示例):
右键单击“引用”>“编辑引用...”,然后单击以选择所需的程序集。
关于 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] 已存在”。
基本数据访问
在 iOS 上运行时,本文档的 DataAccess_Basic 示例代码如下所示:
以下代码演示如何执行简单的 SQLite 操作,并在应用程序的主窗口中以文本形式显示结果。
需要包含这些命名空间:
using System;
using System.IO;
using Mono.Data.Sqlite;
以下代码示例演示整个数据库交互:
- 创建数据库文件
- 插入一些数据
- 查询数据
这些操作通常会出现在代码中的多个位置,例如,可以在应用程序首次启动时创建数据库文件和表,并在应用程序的各个屏幕中执行数据读取和写入。 在以下示例中,已将本示例分组为单个方法:
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();