使用Xamarin开发移动应用示例——数独游戏(六)使用数据库

项目代码可以从Github下载:https://github.com/zhenl/ZL.Shudu 。代码随项目进度更新。
现在我们希望为应用增加更多的功能,比如记录每个完成的游戏,可以让用户自己添加新的数独游戏等等,这些功能需要数据库的支持。我们使用Sqlite数据库保存游戏的数据。Sqlite是基于文件的单机关系型数据库,使用起来非常方便,首先安装程序包sqlite-net-pcl,可以在Visual Studio 2022中使用Nuget管理器安装最新版本,然后,添加POCO类的定义和数据库访问的方法,这里我们需要记录完成的游戏和输入的游戏。在使用数据库时,首先根据指定数据库的路径创建数据库连接,如果数据库文件不存在,则创建新的数据库文件:

readonly SQLiteAsyncConnection _database; _database = new SQLiteAsyncConnection(dbPath);

然后根据定义的POCO创建数据库表(如果不存在):
_database.CreateTableAsync().Wait(); _database.CreateTableAsync().Wait();

接下来就可以使用相应的CRUD函数进行操作了。
下面是记录完成游戏的POCO。数独的初始状态和完成状态是9X9矩阵,由于每个格子只是1-9的数字,采用字符串存储,长度是固定的,恢复为矩阵也比较方便,所以这里采用string类型。完成的步骤是一个列表,每个步骤保存行、列和输入值,这里也用字符串保存,每个步骤之间用指定分隔符隔开。
using SQLite; using System; namespace ZL.Shudu.Services { public class FinishGame { [PrimaryKey, AutoIncrement] public int Id { get; set; } /// /// 游戏的初始状态 /// public string Sudoku { get; set; } /// /// 游戏的结果 /// public string Result { get; set; } /// /// 游戏过程 /// public string Steps { get; set; } /// /// 游戏完成日期 /// public DateTime PlayDate { get; set; } /// /// 使用时间 /// public long TotalTime { get; set; } } }

输入游戏的POCO:
using SQLite; using System; namespace ZL.Shudu.Services { public class InputGameInfo { [PrimaryKey, AutoIncrement] public int ID { get; set; } /// /// 游戏的初始状态 /// public string Sudoku { get; set; } /// /// 输入日期 /// public DateTime InputDate { get; set; } /// /// 是否在游戏中使用 /// public bool UsedInGame { get; set; } } }

操作数据库的类:
using SQLite; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace ShuduApp.Db { public class Database { readonly SQLiteAsyncConnection _database; public Database(string dbPath) { _database = new SQLiteAsyncConnection(dbPath); _database.CreateTableAsync().Wait(); _database.CreateTableAsync().Wait(); }#region InputGameInfo public Task> GetGamesAsync() { return _database.Table().ToListAsync(); }public Task SaveGameAsync(InputGameInfo game) { return _database.InsertAsync(game); }public Task UpdateGameAsync(InputGameInfo game) { return _database.UpdateAsync(game); }public Task DeleteGameAsync(InputGameInfo game) { return _database.DeleteAsync(game); }#endregion#region FinishGame public Task> GetFinishGamesAsync() { return _database.Table().ToListAsync(); }public Task SaveFinishGameAsync(FinishGame game) { return _database.InsertAsync(game); }public Task UpdateFinishGameAsync(FinishGame game) { return _database.UpdateAsync(game); }public Task DeleteFinishGameAsync(FinishGame game) { return _database.DeleteAsync(game); } #endregion } }

完成这些以后,我们改造Game页面,增加保存结果到数据和从数据库产生新游戏的代码。
首先是保存结果到数据库:
private async Task SaveToDb() { try { var finobj = new FinishGame(); for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { finobj.Sudoku += chess[i, j].ToString(); } }for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { finobj.Result += string.IsNullOrEmpty(buttons[i, j].Text) ? "0" : buttons[i, j].Text; } }foreach (var str in steps) { finobj.Steps += str + "; "; }finobj.TotalTime = (DateTime.Now - dtBegin).Ticks; finobj.PlayDate = DateTime.Now; var id=await App.Database.SaveFinishGameAsync(finobj); } catch (Exception ex) {lbMessage.Text = ex.Message; } }

在判断结束的代码中加入上述方法。然后是在初始化时从数据库中获取游戏:
private async Task> LoadFromDb() { var leng = chesses.GetLength(0); var task = App.Database.GetGamesAsync(); var lst = task.Result; for (int k = 0; k < leng; k++) { var sudoku = ""; for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { sudoku += chesses[k, i, j].ToString(); } } var obj = lst.FirstOrDefault(o => o.Sudoku == sudoku); if (obj == null) { await App.Database.SaveGameAsync(new InputGameInfo { Sudoku = sudoku, InputDate = DateTime.Now, UsedInGame = true }); } }var l= await App.Database.GetGamesAsync(); lst = l.Where(o => o.UsedInGame).ToList(); return lst; }

在这里我们判断数据库中是否有游戏,如果没有,将预制的游戏保存到数据库,然后从数据库中生成新的游戏。
我们增加一个新的页面,用来查看现有的游戏:
Item 1 Item 2 Item 3 Item 4 Item 5

后台代码:
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace ZL.Shudu.Views { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class GameList : ContentPage { public ObservableCollection Items { get; set; }public GameList() { InitializeComponent(); RefreshList(); }public async Task RefreshList() { Items = await GetItems(); MyListView.ItemsSource = Items; MyListView.IsVisible = true; }public async Task GetItems() { var items = new ObservableCollection(); var lst = await App.Database.GetGamesAsync(); foreach (var obj in lst) { items.Add(obj.ID.ToString()); } return items; }} }

在导航页面中增加对这个页面的导航:

【使用Xamarin开发移动应用示例——数独游戏(六)使用数据库】使用这个页面,可以查看现有的游戏。
下一步,我们编写查看完成游戏的列表和增加新游戏的页面。

    推荐阅读