文本将通过实现一个记录”收到消息数量”的功能,来演示如何在本 SDK 中操作数据库的 SQLite 数据库。
软硬条件 名 值 IDE VS2017.5 Newbe.Mahua 1.6
业务逻辑 当收到好友消息时,将消息记录在数据库中。
同时将当前数据库中已经存储的消息数目,发送给消息发送者。
实测效果图:
新建项目 使用Newbe.Mahua.Plugins.Template
模板创建项目,项目名称为Newbe.Mahua.Samples.Sqlite
。
新建项目的详细细节,可以参照右侧链接内容:新建项目
业务逻辑实现 业务逻辑比较简单,主要实现两个方法:”保存好友消息”和”获取消息数量”。
为了提升多核 CPU 的利用率,相关接口都采用异步的方式进行定义。实际上时为了让新手看不懂
业务接口代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 using System;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.Sqlite.Services { public interface IFriendMessageStore { Task<int > GetCountAsync () ; Task InsertAsync (InsertFriendMessageInput input ) ; } public class InsertFriendMessageInput { public string Qq { get ; set ; } public string Message { get ; set ; } public DateTimeOffset ReceivedTime { get ; set ; } } }
在MahuaEvents
下添加”好友消息接收事件”,并在事件内调用业务逻辑。实现代码如下:
MahuaEvents 文件夹是本 SDK 建议将事件放置的文件夹位置。也可以不接受建议而添加在其他地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.Sqlite.Services;namespace Newbe.Mahua.Samples.Sqlite.MahuaEvents { public class PrivateMessageFromFriendReceivedMahuaEvent : IPrivateMessageFromFriendReceivedMahuaEvent { private readonly IMahuaApi _mahuaApi; private readonly IFriendMessageStore _friendMessageStore; public PrivateMessageFromFriendReceivedMahuaEvent ( IMahuaApi mahuaApi, IFriendMessageStore friendMessageStore ) { _mahuaApi = mahuaApi; _friendMessageStore = friendMessageStore; } public void ProcessFriendMessage (PrivateMessageFromFriendReceivedContext context ) { _friendMessageStore.InsertAsync(new InsertFriendMessageInput { Message = context.Message, Qq = context.FromQq, ReceivedTime = context.SendTime }).GetAwaiter().GetResult(); var count = _friendMessageStore.GetCountAsync().GetAwaiter().GetResult(); _mahuaApi.SendPrivateMessage(context.FromQq, $"存储中已经存在{count} 条好友信息。" ); } } }
至此业务逻辑便实现完毕。
单元测试 业务逻辑已经实现完毕,接下来对业务逻辑编写单元测试进行验证。其实这么简单的逻辑,看一眼就知道没错
单元测试项目相关的内容可以参看右侧的教程:单元测试
此处只将业务逻辑的关键测试代码展示出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 using Autofac.Extras.Moq;using FluentAssertions;using Moq;using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.Sqlite.MahuaEvents;using Newbe.Mahua.Samples.Sqlite.Services;using System;using System.Threading.Tasks;using Xunit;namespace Newbe.Mahua.Samples.Sqlite.Tests { public class PrivateMessageFromFriendReceivedMahuaEventTests { [Fact ] public void Test () { using (var mocker = AutoMock.GetStrict()) { mocker.VerifyAll = true ; var now = DateTime.Now; var msg = string .Empty; mocker.Mock<IMahuaApi>() .Setup(x => x.SendPrivateMessage("472158246" , It.IsAny<string >())) .Callback<string , string >((qq, inputmsg) => msg = inputmsg); mocker.Mock<IFriendMessageStore>() .Setup(x => x.InsertAsync(It.IsAny<InsertFriendMessageInput>())) .Returns(Task.FromResult(0 )); mocker.Mock<IFriendMessageStore>() .Setup(x => x.GetCountAsync()) .Returns(Task.FromResult(200 )); var service = mocker.Create<PrivateMessageFromFriendReceivedMahuaEvent>(); service.ProcessFriendMessage(new PrivateMessageFromFriendReceivedContext { FromQq = "472158246" , Message = "MSG" , SendTime = now }); msg.Should().Be("存储中已经存在200条好友信息。" ); } } } }
数据库操作实现 定义数据库操作接口 单元测试通过之后便表明当前业务逻辑都已经正确实现了。
接下来进一步就可以实现业务接口的实现类了。
为了完成业务逻辑,本实例至少需要”初始化数据库”、”查询数据库”和”向数据库插入数据”三个数据库操作方法。
其中的”查询数据库”和”向数据库插入数据”可以简单定义为”创建数据库链接即可”。
为了提升多核 CPU 的利用率,相关接口都采用异步的方式进行定义。实际上时为了让新手看不懂
数据库操作接口定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using System.Data.Common;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.Sqlite.Services { public interface IDbHelper { Task InitDbAsync () ; Task<DbConnection> CreateDbConnectionAsync () ; } }
使用 SQLite 实现数据库操作 SQLite 数据库操作,通过官方提供的类库便可以完成。
通过 nuget 安装以下 nuget 包:
System.Data.SQLite.Core Dapper Dapper.Contrib 其中System.Data.SQLite.Core
是数据库驱动,Dapper
则是对ADO.NET
操作的扩展包。
新建应用程序配置文件
。
在应用程序配置文件中配置以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="utf-8" ?> <configuration > <connectionStrings > <add name ="Default" connectionString ="Data Source=|DataDirectory|\mydb.db;Pooling=true;FailIfMissing=false" /> </connectionStrings > <system.data > <DbProviderFactories > <remove invariant ="System.Data.SQLite" /> <add name ="SQLite Data Provider" invariant ="System.Data.SQLite" description =".NET Framework Data Provider for SQLite" type ="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /> </DbProviderFactories > </system.data > </configuration >
添加数据库操作实现类SqliteDbHelper
,详细代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using Dapper;using System;using System.Configuration;using System.Data.Common;using System.Data.SQLite;using System.Diagnostics;using System.IO;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.Sqlite.Services.Impl { internal class SqliteDbHelper : IDbHelper { public Task InitDbAsync () { return Task.Run(() => CreateDbIfnotExists()); } public Task<DbConnection> CreateDbConnectionAsync () { return Task.Run(() => CreateDbConnectionCore()); } private static DbConnection CreateDbConnectionCore () { var dbf = DbProviderFactories.GetFactory("System.Data.SQLite" ); var conn = dbf.CreateConnection(); Debug.Assert(conn != null , nameof (conn) + " != null" ); conn.ConnectionString = ConfigurationManager.ConnectionStrings["Default" ].ConnectionString; return conn; } private static void CreateDbIfnotExists () { var dbDirectory = (string )AppDomain.CurrentDomain.GetData("DataDirectory" ); if (!Directory.Exists(dbDirectory)) { Directory.CreateDirectory(dbDirectory); } var dbfile = Path.Combine(dbDirectory, "mydb.db" ); if (!File.Exists(dbfile)) { SQLiteConnection.CreateFile(dbfile); using (var conn = CreateDbConnectionCore()) { conn.Execute(@" CREATE TABLE MSG( Id TEXT PRIMARY KEY , Qq TEXT NOT NULL, Message TEXT NOT NULL, ReceivedTime TEXT NOT NULL )" ); } } } } }
编写业务实现类 完成了业务接口的定义和数据操作的定义,接下来只要将两者结合起来,便可以实现业务实现类。
添加FriendMessageStore
类,详细代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 using Dapper;using Dapper.Contrib.Extensions;using System;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.Sqlite.Services.Impl { internal class FriendMessageStore : IFriendMessageStore { private readonly IDbHelper _dbHelper; public FriendMessageStore (IDbHelper dbHelper ) { _dbHelper = dbHelper; } public async Task<int > GetCountAsync () { using (var conn = await _dbHelper.CreateDbConnectionAsync()) { var count = await conn.ExecuteScalarAsync<int >("select count(1) from MSG" ); return count; } } public async Task InsertAsync (InsertFriendMessageInput input ) { using (var conn = await _dbHelper.CreateDbConnectionAsync()) { await conn.InsertAsync(new MessageEntity { Id = Guid.NewGuid().ToString(), Message = input.Message, Qq = input.Qq, ReceivedTime = input.ReceivedTime.ToString("s" ) }); } } [Table("MSG" ) ] public class MessageEntity { [Key ] public string Id { get ; set ; } public string Qq { get ; set ; } public string Message { get ; set ; } public string ReceivedTime { get ; set ; } } } }
在插件启动时初始化数据库 数据库的初始化,需要在插件启动时进行调用。
在MahuaEvents
下添加”插件初始化事件”,并在事件内调用业务逻辑。实现代码如下:
MahuaEvents 文件夹是本 SDK 建议将事件放置的文件夹位置。也可以不接受建议而添加在其他地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.Sqlite.Services;namespace Newbe.Mahua.Samples.Sqlite.MahuaEvents { public class InitializationMahuaEvent : IInitializationMahuaEvent { private readonly IDbHelper _dbHelper; public InitializationMahuaEvent ( IDbHelper dbHelper ) { _dbHelper = dbHelper; } public void Initialized (InitializedContext context ) { _dbHelper.InitDbAsync(); } } }
模块注册 以上所有的接口与实现类与接口,都不要忘记在模块中进行注册,以下是MahuaModule
的完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 using Autofac;using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.Sqlite.MahuaEvents;using Newbe.Mahua.Samples.Sqlite.Services;using Newbe.Mahua.Samples.Sqlite.Services.Impl;using System;using System.IO;namespace Newbe.Mahua.Samples.Sqlite { public class MahuaModule : IMahuaModule { public Module[] GetModules () { AppDomain.CurrentDomain.SetData("DataDirectory" , Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data" )); return new Module[] { new PluginModule(), new MahuaEventsModule(), new MyServiceModule() }; } private class PluginModule : Module { protected override void Load (ContainerBuilder builder ) { base .Load(builder); builder.RegisterType<PluginInfo>() .As<IPluginInfo>(); } } private class MahuaEventsModule : Module { protected override void Load (ContainerBuilder builder ) { base .Load(builder); builder.RegisterType<PrivateMessageFromFriendReceivedMahuaEvent>() .As<IPrivateMessageFromFriendReceivedMahuaEvent>(); builder.RegisterType<InitializationMahuaEvent>() .As<IInitializationMahuaEvent>(); } } private class MyServiceModule : Module { protected override void Load (ContainerBuilder builder ) { base .Load(builder); builder.RegisterType<FriendMessageStore>() .As<IFriendMessageStore>(); builder.RegisterType<SqliteDbHelper>() .As<IDbHelper>(); } } } }
集成测试 万事具备,只欠生成。
生成解决方案,运行build.bat
,复制相关的 DLL 到对应的平台,向机器人发送消息,效果达成!
以下是 CQP 平台的测试效果。其实其他的没测试
总结 数据库操作本身并不困难。
开发过程中采用基于接口开发的基本思想,结合单元测试,不论是开发简单的插件还是复杂的项目,都是可靠的方法。
若 SQLite 无法满足项目要求,只要将多实现一个IDbHelper
便可以完成了,开发者可以动手体验。
实例的项目代码,可以在源码仓库中的Newbe.Mahua.Samples
解决方案下找到。
教程链接 发布说明