在Visual Studio开发的Xamarin.Android应用程序中使用Sqlite






4.72/5 (22投票s)
在Visual Studio开发的Xamarin.Android应用程序中使用Sqlite
引言
如果你曾使用Visual Studio的Xamarin插件开发过Android应用程序,那么你很可能遇到过存储和检索数据的需求。尽管你的应用程序最初可能通过网络服务接收数据,但你可能希望将数据存储在设备上,这样就不必 계속 从网络服务请求数据了。除非你的数据高度易变,否则将数据存储在设备数据库中是有意义的。从本地存储检索数据会比通过网络服务检索远程数据更快,并且如果你处于信号不佳的区域,也不会受到连接问题的困扰。由开发人员决定确切要本地存储哪些数据以及更新这些数据的频率。
背景
Android内置了与数据库Sqlite的集成。如果你还没有听说过Sqlite,我建议你在继续阅读之前先了解一下。
什么是Sqlite?
简而言之,Sqlite是一个开源且广泛使用的数据库管理引擎,占用空间极小,非常适合在移动应用程序中使用。它是无服务器、自包含的,并且无需配置。它支持标准关系数据库特性,例如预处理语句和事务,并提供SQL语法。
Sqlite和Android
Sqlite已集成到所有Android设备中,因此Sqlite数据库不需要任何配置或设置。一旦你定义了数据库的结构,它就会由Android为你自动管理。你的数据库定义可以包含表和索引。
与所有数据库引擎一样,每次访问表或索引时,你实际上都在访问文件系统。因此,在设计数据库时需要考虑这些限制。例如,创建数据库连接本质上是访问存储在设备本地的文件。因此,你应该意识到这样的操作可能比不访问本地文件系统的其他操作花费相对更长的时间才能完成。出于这个原因,考虑异步执行此类任务是个好主意。
与任何数据库工具一样,Sqlite可以根据应用程序的需求以多种不同方式使用。本文演示了在你的Xamarin.Android
应用程序中使用Sqlite的一种方式,但这绝不是唯一的方式。
将Sqlite添加到你的应用程序
要在应用程序中包含Sqlite功能,你需要向代码添加以下引用
using Android.Database.Sqlite;
你还需要向项目添加一个Sqlite.cs源文件。你可以从GitHub获取代码。
将此文件添加到Visual Studio项目后,你需要添加以下引用以使用其中包含的功能
using SQLite;
创建和更新你的数据库
以下代码展示了一个骨架示例,说明你的数据库如何在Android中创建和更新。需要注意的关键点是
- 创建
SQLiteOpenHelper
的子类 - 为虚方法
OnCreate()
提供实现 - 为虚方法
OnUpgrade()
提供实现
要使用SQLite创建数据库,你需要创建SQLiteOpenHelper
的子类。这是一个用于管理数据库创建和版本控制的Android帮助类。
在你新创建的子类中,你需要为方法OnCreate()
和OnUpgrade()
提供实现。
OnCreate()
在你的应用程序首次安装时调用OnUpgrade()
在你的应用程序升级时调用
你的OnCreate()
方法应包含创建数据库实体(如表和索引)所需的所有代码。你的OnUpgrade()
方法应包含当你对数据库结构进行后续更改时修改数据库的代码。请注意,这两个方法都接受一个SQLiteDatabase
参数。
另请注意,OnUpgrade()
方法还接受两个int
参数。第一个int
参数是当前(已安装)数据库版本。第二个int
参数是新的(升级)数据库版本。
你的OnUpgrade()
方法应该这样编码:它首先检查最小(最早)的数据库版本,然后逐渐升高,即,数据库升级应该首先对最早的版本进行更改,然后逐渐升高,直到达到倒数第二个数据库版本。
using Android.Database.Sqlite;
namespace DataManager
{
public class DataManagerHelper : SQLiteOpenHelper
{
// specifies the database name
private const string DatabaseName = "myDatabaseName";
//specifies the database version (increment this number each time you make database related changes)
private const int DatabaseVersion = 3;
public DataManagerHelper(Context context)
: base(context, DatabaseName, null, DatabaseVersion)
{
}
public override void OnCreate(SQLiteDatabase db)
{
//create database tables
db.ExecSQL(@"
CREATE TABLE IF NOT EXISTS Customer (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
FirstName TEXT NOT NULL,
LastName TEXT NOT NULL )");
//create database indexes
db.ExecSQL(@"CREATE INDEX IF NOT EXISTS FIRSTNAME_CUSTOMER ON CUSTOMER (FIRSTNAME)");
}
public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
if (oldVersion < 2)
{
//perform any database upgrade tasks for versions prior to version 2
}
if (oldVersion < 3)
{
//perform any database upgrade tasks for versions prior to version 3
}
}
}
}
创建应用程序类
为应用程序的每个表创建一个单独的类是一个好习惯。例如,我们刚刚在OnCreate()
方法中创建了一个名为Customer
的表。要在整个应用程序中访问此表中的数据,请创建一个映射到该表的类。此表也将被称为Customer
。
using System;
using SQLite;
namespace DataManager
{
public class Customer
{
[PrimaryKey, AutoIncrement]
public long Id { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
}
}
设置应用程序上下文
在我们调用任何数据库功能之前,我们上面创建的SQLiteOpenHelper
子类需要知道应用程序的上下文才能工作。在我们的例子中,我们需要将应用程序上下文设置为我们的子类DataManagerHelper
。
在我们的数据库代码中,我们需要添加一个接受Android Context
作为参数的方法。此方法需要在进行任何数据库调用之前调用。
using SQLite;
namespace DataManager
{
public class DatabaseUpdates
{
private DataManagerHelper _helper;
public void SetContext(Context context)
{
if (context != null)
{
_helper = new DataManagerHelper(context);
}
}
}
}
上述方法需要在进行任何数据库调用之前调用。因此,你可能希望考虑从应用程序的OnCreate()
事件中添加对此数据库函数的调用。
namespace myApplication
{
[Application]
public class myApplication : Application
{
public override void OnCreate()
{
base.OnCreate();
//This method needs to be called before any database calls can be made!
DatabaseManager mydata = new DatabaseManager();
mydata.SetContext(this);
}
}
}
更新数据
到目前为止,我们已经创建了数据库表,并创建了映射到这些表且我们可以在应用程序中使用的类。下一步是实现从表中添加/编辑/删除数据的代码。将这些定义与我们上面创建的数据定义类分开放在一个单独的类和程序文件中是一个好习惯。
以下是一些从数据库表中添加/更新/删除数据的示例方法。
using SQLite;
namespace DataManager
{
public class DatabaseUpdates
{
private DataManagerHelper _helper;
public long AddCase(Customer addCust)
{
using (var db = new SQLiteConnection(_helper.WritableDatabase.Path))
{
try
{
return db.Insert(addCust);
}
catch (Exception ex)
{
//exception handling code to go here
}
}
}
public long DeleteCase(Customer deleteCust)
{
using (var db = new SQLiteConnection(_helper.WritableDatabase.Path))
{
try
{
return db.Delete(deleteCust);
}
catch (Exception ex)
{
//exception handling code to go here
}
}
}
public long UpdateCustomer(Customer updCust)
{
using (var db = new SQLiteConnection(_helper.WritableDatabase.Path))
{
try
{
return db.Update(updCust);
}
catch (Exception ex)
{
//exception handling code to go here
}
}
}
}
}
查询数据
除了更新表外,我们还需要从表中选择和检索数据。我们可以使用LINQ-to-SQL来实现此类查询。以下是一些示例,演示了我们如何查询数据库表。
using System.Collections.Generic;
using SQLite;
namespace DataManager
{
public class DatabaseUpdates
{
private DataManagerHelper _helper;
//Get the total number of orders for a specific customer
public int GetTotalOrderCount(string custNo)
{
using (var db = new SQLiteConnection(_helper.ReadableDatabase.Path))
{
try
{
if (!string.IsNullOrEmpty(custNo))
{
int count = db.Table<Orders>().Count(c => c.CustNo == custNo);
return count;
}
return 0;
}
catch (Exception ex)
{
return 0;
}
}
}
//retrieve a specific user by querying against their first name
public Customer GetUser(string firstname)
{
using (var database = new SQLiteConnection(_helper.ReadableDatabase.Path))
{
try
{
return database.Table<Customer>().FirstOrDefault(u => u.FirstName == firstname);
}
catch (Exception ex)
{
//exception handling code to go here
}
}
}
//retrieve a list of all customers
public IList<customer> GetAllCustomers()
{
using (var database = new SQLiteConnection(_helper.ReadableDatabase.Path))
{
try
{
return database.Table<Customer>().ToList();
}
catch (Exception ex)
{
//exception handling code to go here
}
}
}
}
}
Sqlite中有两种连接类型。
- 只读连接 - 用于检索/查询数据时使用
- 读写连接 - 用于更新表中的数据时使用,即执行更新/添加/删除操作时使用
获取只读连接
using (var db = new SQLiteConnection(_helper.ReadableDatabase.Path))
{
//code to go here
}
获取读写连接
using (var db = new SQLiteConnection(_helper.WritableDatabase.Path))
{
//code to go here
}
你可以使用的一种可能的命名约定是,根据它们执行的操作,在所有数据库函数前加上Delete、Update、Add或Get。然后,你可以将它们作用的实体作为函数名称的第二部分。
即<ACTION><ENTITY>
例如
AddCustomer
UpdateCustomer
DeleteCustomer
GetCustomer
AddOrder
UpdateOrder
DeleteOrder
GetOrder
处理数据库函数中的异常
为了清晰起见,并使代码简洁易懂,我到目前为止的示例中省略了异常处理代码。但是,你需要某种方式来处理代码抛出异常的情况。这可能是由于数据库锁定等原因造成的。一种简单的异常处理策略是等待一小段时间,然后再次尝试该函数。
我在自己的应用程序中注意到,如果从紧密循环中访问表,Sqlite并非总能在请求另一个锁之前释放锁,Sqlite会抛出与忙碌或锁定相关的错误消息。等待一小段时间后再次尝试锁定似乎是一种可接受的策略。
using System.Threading;
using SQLite;
public long AddCase(Customer addCust)
{
using (var db = new SQLiteConnection(_helper.WritableDatabase.Path))
{
try
{
return db.Insert(addCust);
}
catch (Exception ex)
{
//wait half a second and then try again
Thread.Sleep(500);
return AddCase(addCust);
}
}
}
你可能还希望记录错误,以便记录它们。你可以使用Android日志系统来完成此操作。
摘要
希望本文能为你提供足够的信息,以便你开始在自己的Xamarin.Android
应用程序中使用Sqlite。如果你希望我进一步阐述本文中的任何内容,请随时留言。