百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

使用 SQLite 在本地存储数据 如何使用SQLite保存数据

liebian365 2024-11-13 13:25 22 浏览 0 评论

拥有关系数据时,在 SQLite 中存储数据十分有用。 例如,假设正在生成用于管理图书馆的应用程序。 图书馆中的每本书都有一位或多位作者,而一名作者可以写多本书。 可在 SQLite 数据库中轻松地为这种关系建模。

本单元将介绍如何通过 SQLite.NET 在 Xamarin 应用程序中使用 SQLite。

什么是 SQLite?

SQLite 是轻型跨平台本地数据库,其已成为移动应用程序的行业标准。 SQLite 不在服务器上运行,并存储在设备文件系统上的单一磁盘文件中。 所有读写操作都直接针对 SQLite 磁盘文件运行。

SQLite 本机库默认内置于 Android 和 iOS 中;但其引擎只支持 C/C++ API。 对于想要 SQLite 和 .NET 通过某种方式进行交互的 .NET 开发人员来说,此方案并不理想。

备注:如果想要下载 SQLite.NET,可在此处找到它:https://www.nuget.org/packages/sqlite-net


什么是 SQLite.NET?

C++学习资料→CC++入门到高级资料

本机 SQLite 引擎有多个 C# 包装器可供 .NET 开发人员使用。 许多 Xamarin 开发人员使用名为 SQLite.NET 的常用 C# 包装器。

SQLite.NET 是对象关系映射器。 它通过让我们能够将在项目中定义的模型用作架构,帮助简化定义数据库架构的过程。 以下面的代码片段为例:

class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    ...
}

借助对象关系映射器,可使用此初始“User” 类,并让其自动创建包含“Id”和“Username”列的名为“User”的数据库表。

SQLite.NET 作为 NuGet 包提供。 将它添加到 Xamarin.Forms 的每个项目中。

如何连接到 SQLite 数据库

SQLite.Net 通过“SQLiteConnection”对象建立与 SQLite 数据库的连接。 实例化此对象时,必须传入数据库文件的文件名。 然后,它将打开文件(如果存在文件)或创建文件(如果不存在文件)。

下面是其用法示例:

string filename = ...
SQLiteConnection conn = new SQLiteConnection(filename);

请注意,filename 需要指向应用沙盒中的位置。

如何创建表

回想一下,SQLite.NET 是对象关系映射器,这意味着可以通过 C# 类生成数据库架构。 返回 User 类的上一个示例。

class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    ...
}

SQLite.NET 可通过此 C# 类生成数据库架构,但许多属性可供添加到类中,用于对架构进行修改。

以下是可用属性的一些示例:

  • Table:如果希望表名不是类名,请指定表的名称。
  • primaryKey:指定要为主键的列。
  • AutoIncrement:指定在插入新行时,列值应自动增加。
  • Column:如果不想使用属性名作为列名,请指定列的名称。
  • MaxLength:指定列中可使用的最大字符数。
  • Unique:指定列中的值必须与其他所有行不同。

回到“User”类,下面是使用所有这些属性的更新版本:

[Table("user")]
public class User
{
    // PrimaryKey is typically numeric 
    [PrimaryKey, AutoIncrement, Column("_id")]
    public int Id { get; set; }

    [MaxLength(250), Unique]
    public string Username { get; set; }
    ...
}

定义要用作数据库架构的 C# 类后,需要让 SQLite.NET 创建表。 为此,请在“SQLiteConnection”类上使用“CreateTable”方法。 下面是一个示例:

SQLiteConnection conn = new SQLiteConnection(filename);
conn.CreateTable<User>();

如果调用“CreateTable”方法且表已存在于数据库中,则它将检查架构类,查看是否有任何更改。 如果有任何更改,将执行更新操作,尝试更新数据库架构。

如何执行基本读写操作

创建表后,即可开始与它进行交互。 首先可插入一些数据。 为此,请在 SQLiteConnection 实例上使用 Insert 方法。 例如,如果想要将新的“User”插入数据库,则代码将类似于以下示例:

public int AddNewUser(User user)
{
    int result = conn.Insert(user);
    return result;
}

“Insert”方法返回“int”,其表示插入表中的行数。 在本例中,该数字为 1。

插入数据后,通常需要从表中检索数据。 借助 SQLite.NET,可使用“Table”方法轻松地在表中检索所有行。 以下是如何使用它的示例:

public List<User> GetAllUsers()
{
    List<User> users = conn.Table<User>().ToList();
    return users;
}

“Table”方法返回“TableQueryT”。 若要获取“List”,请使用“ToList”方法。

使用 LINQ 执行 SQLite 查询

虽然可使用“Table”方法在表中检索所有行,但并不总是想要这样做。 有时,想要仅返回行的子集或运行更复杂的查询。 对于这些任务,请将 LINQ 与 SQLite.NET 配合使用。

SQLite.NET 支持许多常见的 LINQ 查询,其中包括:

  • Where
  • Take
  • Skip
  • OrderBy
  • OrderByDescending
  • ThenBy
  • ElementAt
  • First
  • FirstOrDefault
  • ThenByDescending
  • Count

通过这些方法,你可以使用扩展方法语法或 LINQ C# 语法。 例如,下面是可供使用 LINQ C# 语法按用户名获取用户的代码片段:

public User GetByUsername(string username)
{
    var user = from u in conn.Table<Person>()
               where u.Username == username
               select u;
    return user.FirstOrDefault();
}

定义 SQLite.NET 实体

首先,创建用于定义数据库架构的模型类。

  1. 将名为“Models”的新文件夹添加到“People”.NET 标准库中。
  2. 在“Models”文件夹中,创建名为“Person”的新类。 确保将其标记为“公开”。
namespace People.Models
{
   public class Person
   {
   }
}

3.添加名为“ID”的 int 属性。

4.添加名为 Name 的字符串属性。

namespace People.Models
{
   public class Person
   {
      public int Id { get; set; }
      public string Name { get; set; }
   }
}

添加 SQLite.NET 属性

现在已创建了模型,接下来可添加一些属性用于帮助 SQLite.NET 将类映射到表中。

  1. 为 SQLite 命名空间添加 using 指令。 借助该指令,可使用 SQLite.NET 属性。
  2. 使用 [Table] 属性批注“Person”类,并将名称指定为“people”。
  3. 将“Id”属性指定为主键。 使用 [PrimaryKey] 和 [AutoIncrement] 属性对其进行批注。
  4. 向“Name”属性添加注释,以向数据添加一些约束。 将其 [MaxLength] 指定为 250。 指定列中的每个值都应为 [Unique]。
using SQLite;

namespace People.Models
{
   [Table("people")]
   public class Person
   {
      [PrimaryKey, AutoIncrement]
      public int Id { get; set; }

      [MaxLength(250), Unique]
      public string Name { get; set; }
   }
}

5.生成应用程序,确保其能正常编译。


添加存储库类

接下来,需要为跨平台项目添加一些预生成的代码。 已提供此代码。 您可以在练习存储库的克隆或下载副本的文件夹中找到它。

  1. 转到“exercise1”“start”文件夹。
  2. 打开“People.sln”解决方案。
  3. 将“assets”文件夹中的“PersonRepository.cs”C# 源文件添加到“People”共享项目中。 可将其从文件夹拖放到解决方案资源管理器中的项目根节点。
  4. 检查存储库类。 它具有一些预先提供的方法和少量 TODO 标记,将在其中添加一些功能用于访问数据库。

连接到数据库

现在,将在“SQLiteConnection”上创建一个实例,然后创建一个“Person”表。

  1. 打开“PersonRepository.cs”源文件。
  2. 在构造函数中,初始化新的“SQLiteConnection”。 将其分配给名为“conn”的字段。
  3. 使用“conn.CreateTable”方法创建一个表,用于存储“Person”数据。
private SQLiteConnection conn;
...
public PersonRepository(string dbPath)
{
   conn = new SQLiteConnection(dbPath);
   conn.CreateTable<Person>();
}

向数据库中插入行

创建“Person”表后,即可开始插入数据。 实现“AddNewPerson”方法,使用户能够插入新人员。

  1. 在“PersonRepository.cs”中,找到“AddNewPerson”方法。
  2. 添加代码以插入新的“Person”对象。 在“SQLiteConnection”对象上使用“Insert”方法。 将返回值分配给已在方法中定义的“result”变量。
public void AddNewPerson(string name)
{
   int result = 0;
   try
   {
      //basic validation to ensure a name was entered
      if (string.IsNullOrEmpty(name))
            throw new Exception("Valid name required");

      result = conn.Insert(new Person { Name = name });
      ...
   }
   ...
}

从数据库中读取

此时,可以向“Person”表添加新行。 尝试使用“Table”方法读取它们。

  1. 在“PersonRepository.cs”中,找到“GetAllPeople”方法。
  2. 使用 Table<T> 方法检索所有记录。
  3. 无法直接返回枚举器,因为预期为 List<Person>。 使用“ToList()”扩展方法将其转换为有效类型。
  4. 可根据需要,通过将代码包装到 try-catch 块中来包含错误处理流程。 如果存在错误,请将“StatusMessage”属性设置为异常的“消息”并返回空的 。
public List<Person> GetAllPeople()
{
   try
   {
      return conn.Table<Person>().ToList();
   }
   catch (Exception ex)
   {
      StatusMessage = string.Format("Failed to retrieve data. {0}", ex.Message);
   }

   return new List<Person>();
}

连接到 UI

最后一步是添加 UI,以调用在上一步中实现的方法。 使用练习所用的“Assets”文件夹中包含的预生成的 UI。

  1. 将“People”共享项目中的“MainPage.xaml”和“MainPage.xaml.cs”替换为资产中的副本。 将它们直接复制到文件夹结构中并替换现有文件。
  2. 打开 .NET Standard 项目中的“App.xaml.cs”文件,然后找到构造函数。
  3. 将传入的参数重命名为 dbPath(如果它不是该名称)。
  4. 删除“MainPage”对象上的“Text”属性资源库。 你不再拥有该属性,因为刚刚替换了 UI。
  5. 添加名为“PersonRepo”的公共静态属性,用于保存“PersonRepository”对象。
  6. 通过创建“PersonRepository”的实例来初始化构造函数中的“PersonRepo”属性,并将“dbPath”参数传入存储库构造函数。

运行应用程序

生成解决方案,并运行应用程序。 通过在文本框中键入名称并选择“添加人员”,将人员添加到数据库中。

之后,可选择“获取所有人员”,以从数据库中提取名称并将在列表中显示它们。 此外,尝试重启应用程序,查看数据在应用程序启动期间是否还在。


如果以同步方式对数据库运行查询,可能会导致性能问题。 SQLite.NET 具有一个异步 API,可用于使应用程序始终保持响应。

本单元将介绍如何使用 SQLite.NET 的异步 API 来确保应用程序保持高度响应。

了解异步查询

到目前为止,所做的一切均已在 UI 线程上执行。 但是,若要生成响应速度极快的移动应用程序,需要以略有不同的方式执行操作。 如果在 UI 线程上运行数据库操作,则可能会导致 UI 在操作需要很长时间才能完成时冻结。

为了解决此问题,SQLite.NET 通过“SQLiteAsyncConnection”类包含了一个异步 API。 例如,若要异步创建表,可使用

var conn = new SQLiteAsyncConnection(dbPath);
await conn.CreateTableAsync<User>();

SQLite.NET 不是线程安全的。 这意味着如果多个线程使用同一个连接,你可能会遇到问题。 因此,最好有一个负责管理“SQLite”连接的存储库类。

使用 SQLite.NET 执行异步操作

“SQLiteAsyncConnection”公开与同步对应类相同的操作。 但是,操作均基于任务,以便在后台使用。

下面列出了一些常见的异步操作:

  • CreateTableAsync:根据指示的类创建表
  • DropTableAsync:删除与指示的类关联的表
  • GetAsync:获取表中与指示的类关联的记录,并匹配传入构造函数的主键
  • InsertAsync:使用传入构造函数的项插入新记录
  • UpdateAsync:使用传入构造函数的项更新现有记录
  • DeleteAsync:删除表中映射到指示类的记录,并匹配传入构造函数的主键
  • QueryAsync:运行直接 SQL 查询并返回对象
  • ExecuteAsync:运行直接 SQL 查询并返回受影响的行数。
  • ExecuteScalarAsync:运行直接 SQL 查询并返回单个结果
  • ToListAsync:异步执行“Table”方法

以下是使用“ToListAsync”方法异步检索记录的示例:

SQLiteAsyncConnection conn;
ObservableCollection<User> userList;  // Bound to UI
...
public async Task AddAllUsersAsync()
{
    List<User> users = await conn.Table<User>().ToListAsync();
    // Must be on UI thread here!
    foreach (var u in users)
        userList.Add(u);
}

使用“ToListAsync”方法从数据库异步获取所有用户。 如果使用此方法,即使数据库中有大量用户,UI 也能保持响应。

异步使用 SQLite

本单元将把 Person 存储库应用程序从同步 SQLite.NET API 转换为异步版本。 这样一来,无论对数据库执行多少次查询,应用程序将始终能够保持响应。

创建异步连接

首先将“PersonRepository”更改为使用“SQLiteConnection”的异步版本。 现在,可以与数据库进行异步交互。

  1. 打开“People”.NET Standard 项目中的“PersonRepository.cs”文件。
  2. 将“SQLiteConnection”conn 属性更改为“SQLiteAsyncConnection”。 若要进行此更改,必须更新属性和实例化。
  3. 在构造函数中,请注意,对“CreateTable”的调用不再有效。 将此调用替换为“CreateTableAsync”。
private SQLiteAsyncConnection conn;

public PersonRepository(string dbPath)
{
   conn = new SQLiteAsyncConnection(dbPath);
   conn.CreateTableAsync<Person>().Wait();
}

以异步方式向表中插入项

现在使用的是“SQLiteAsyncConnection”,可与数据库进行异步交互。 接下来了解如何以异步方式插入新项。

修改“AddNewPerson”方法,以通过异步插入方式插入新的“Person”。

using System.Threading.Tasks;
...
public async Task AddNewPersonAsync(string name)
{
   int result = 0;
   try
   {
      //basic validation to ensure a name was entered
      if (string.IsNullOrEmpty(name))
            throw new Exception("Valid name required");

      // TODO: insert a new person into the Person table
      result = await conn.InsertAsync(new Person { Name = name });

      StatusMessage = string.Format("{0} record(s) added [Name: {1})", result, name);
   }
   catch (Exception ex)
   {
      StatusMessage = string.Format("Failed to add {0}. Error: {1}", name, ex.Message);
   }
}

异步获取表中的所有项

最后,从数据库中异步检索“People”。

  1. 修改“GetAllPeople”方法,以使用异步调用返回结果。
public async Task<List<Person>> GetAllPeopleAsync()
{
   try
   {
      return await conn.Table<Person>().ToListAsync();
   }
   catch (Exception ex)
   {
      StatusMessage = string.Format("Failed to retrieve data. {0}", ex.Message);
   }

   return new List<Person>();
}

2.在“MainPage.xaml.cs”文件中,修改两个“Button.Click”事件处理程序,以使用“PersonRepository”类中的异步方法。 使用 async 和 await 关键字。

3.运行该程序,验证它是否仍能正常运行。

总结

在移动设备本地存储数据对于提高性能十分有用。 可以在本地存储重要数据并快速检索,而不是通过不断地调用远程服务器来获取数据。

可用的存储选项因拥有的数据类型而异。 处理本质上为关系数据的数据时,数据库是最佳选项。

可使用 SQLite 在 Xamarin 应用程序中创建本地数据库。 SQLite.NET 是 SQLite 的 C# 包装器。 它公开异步 API,以帮助确保应用程序的 UI 始终保持响应。

相关推荐

记录一个ComboBox的设置问题,你可能没遇到过

ComboBox这个控件使用频率太高了,我从VC6编程开始就用它,一直用到C#到现在的Net6,要说我这么一个编程老手还能在它身上栽跟头,我都不敢相信。但是今天竟然被它无情的戏耍了。记录下这个问题,看...

组合框(Combo Box)应用之一_combo简单组合框

【分享成果,随喜正能量】对别人期待太高,本质上是对自身无能的逃避和推托,与其期待别人,不如依靠自己。你不害怕孤独,就不再寄期望于他人陪伴;你有底气解决问题,就不在寄期望于他人向你伸出援手。一个人期待值...

Qt之QComboBox定制(二)_qt on_combobox_activated

上一篇文章Qt之QComboBox定制讲到了qt实现自定义的下拉框,该篇文章主要实现了列表式的下拉框,这一节我还将继续讲解QComboBox的定制,而这一节我将会讲述更高级的用法,不仅仅是下拉列表框,...

从零开始系列,用C#做软件产品:私人日记(九)ComboBox入门

第八节的内容早已写好发布,结果一直在审核中,不知道触动了哪条神经。评论中看到有一些网友都在问为什么不用WPF来开发,在这里我统一说明下:1)WPF界面设计相对复杂。由于它是矢量的,需要额外有很多容器做...

QT-QSharedMemory_qt450-10是什么材料

1.QSharedMemory介绍...

进入Python的世界19-pyqt6不只是UI设计,其他模块功能如何运用

今天是大年初四,继续探讨pyqt6,给出使用的建议。PyQt6绝不仅仅局限于UI设计...

从零开始学Qt - 10:一文读懂Qt的元对象系统

Qt本身并不是一种编程语言,它实质上是一个跨平台的C++开发类库。它是用标准C++编写的,为开发GUI应用程序和非GUI应用程序提供了各种类。Qt对标准C++进行了扩展,引入一些新的概念和功能,例如信...

QT实现抖动文字和滚动文字,附源码

前言不知道大家有没有发现今天的文章有什么不一样,哈哈,我自己胡拼乱凑弄了一个logo,好不好看就先不说了,最起码萌萌哒...当然这不是今天的重点,在做logo的时候,我原本想让文字动起来的,奈何技术有...

Qt Concurrent的使用_qt是什么意思

1.简介QtConcurrent命名空间提供了高级api,使得无需使用诸如互斥、读写锁、等待条件或信号量等低级线程原语就可以编写多线程程序。使用QtConcurrent编写的程序会根据可用的...

Qt使用FFmpeg播放视频_qt使用ffmpeg播放视频功能

一、使用场景...

Qt中使用匿名函数lambda表达式_匿名函数lambda

一、为什么要使用匿名函数lamdba首先,lambda表达式可以使代码变得简单,C++中,一个lambda表达式表示一个可调用的代码单元。如代码:...

EtherCAT运动控制卡开发教程之Qt(中):小线段连续轨迹加工

今天,正运动小助手给大家分享一下EtherCAT运动控制卡开发教程之Qt,主要介绍一下如何通过Qt编程实现小线段轨迹连续加工,暂停与继续。...

送亲人,用1小时制作精美电子相框 | Qt 示例

今天给大家分享:...

Python之面向对象:综合应用,基于GUI实现会动的游戏英雄

引言本打算以上一篇文章作为面向对象模块的收尾,但是,犹豫了许久,还是决定再补充一篇,也就是今天这篇文章,打算基于Python的PyQt6/PySide6框架开发一个GUI程序,模拟实现一个在电脑桌面活...

Qt——常用数据类型_qt基本数据类型

1.基础类型因为Qt是一个C++框架,因此C++中所有的语法和数据类型在Qt中都是被支持的,但是Qt中也定义了一些属于自己的数据类型,下边给大家介绍一下这些基础的数类型。...

取消回复欢迎 发表评论: