AppBoxFuture(低程式码快速开发框架): 另类的ORM实现
通常的ORM实现基于配置或注释,由反射或Emit生成相应的Sql语句,然后将Sql传送给数据库解析Sql字串生成AST再交给优化器处理后执行,返回的资料再经由反射或Emit转换为相应的实体例项。作者认为上述方式主要存在以下两个问题:
实体类程式码是硬编码的,如果实体类定义变更必须重新编译应用再部署,不利于实现执行时动态变更实体定义;CRUD操作转换为Sql的实现复杂,且需要针对不同的数据库做适配优化。 由于作者追求极致简单的系统架构以及丝般顺滑的开发体验,所以采用了另类的方式在框架内实现了ORM,之于另类在什么地方我们先通过一些简单示例后再来说明一下实现原理。
一、CRUD操作
还是用系统自带的Emploee模型作为示例,在IDE新建服务模型新增以下程式码储存释出后可通过主选单->Service->Invoke执行测试:
//事务新建两条记录
var emp1 = new Entities.Emploee();
emp1.Name = "Rick";
emp1.Account = "[email protected]";
emp1.Birthday = new DateTime(1977, 3, 16);
var emp2 = new Entities.Emploee();
emp2.Name = "Johne";
emp2.Account = "[email protected]"
emp2.Birthday = new DateTime(1979, 1, 2);
using(var txn = await Transaction.BeginAsync())
{
await EntityStore.SaveAsync(emp1, txn);
await EntityStore.SaveAsync(emp2, txn);
await txn.CommitAsync();
}
//查询记录
var q = new TableScan();
q.Filter(t => t.Name == "Rick");
var emps = await q.ToListAsync();
//更新记录
emps[0].Name = "Rick Lu";
await EntityStore.SaveAsync(emps[0]);
//删除记录
await EntityStore.DeleteAsync(emps[0]);
以上只是已实现的一些Api示例,复杂的如根据索引查询、聚合查询等Api正在设计开发中。
二、实现原理
设计时:这部分实现重度依赖Roslyn功能,服务端在开发人员登入至IDE后会通过Roslyn生成虚拟专案。

实体模型储存时服务端生成虚拟的实体类程式码,并加入虚拟专案内;服务模型储存时服务端生成虚拟的服务类程式码,并加入虚拟专案内;释出时服务端的编译引擎解析虚拟的服务程式码,然后转换为执行时服务程式码,再通过Roslyn编译为动态服务元件。其中虚拟程式码转换为执行时程式码的过程主要是:将实体属性取赋值操作(eg: entity.Name = "aa" 或 var temp = entity.Name)转换为针对执行时Entity类的GetXXX()及SetXXX()操作。**这里需要注意的是执行时只存在一个Entity类(类似于KVO通过字典表储存属性值)**;将查询条件转换为可序列化的表示式,这样储存引擎执行查询命令时可委托clr emit生成过滤指令,储存引擎在扫描时直接使用过滤指令计算满足条件的记录。执行时:
这部分实现参考以下流程图,需要注意的是储存引擎是基于RocksDB的,实体资料转换为KV资料(如Key=Id, Value=[字段标识:字段值;字段标识:字段值]),这样转换过程就不需要使用反射或Emit。

三、效能测试
作者做了简单的效能测试(单节点I74C8G虚拟机器):并发插入不带索引不带外来键的简单实体约28000tps;通过惟一索引查询约80000qps;带条件扫描少量记录约35000qps。