一般的ORM完成依据装备或注释,由反射或Emit生成相应的Sql句子,然后将Sql发送给数据库解析Sql字符串生成AST再交给优化器处理后履行,回来的数据再经由反射或Emit转化为相应的实体实例。作者以为上述方法首要存在以下两个问题:

  1. 实体类代码是硬编码的,假如实体类界说改变有必要从头编译运用再布置,不利于完成运转时动态改变实体界说;
  2. CRUD操作转化为Sql的完成杂乱,且需求针对不同的数据库做适配优化。

  因为作者寻求极致简略的体系架构以及丝般顺滑的开发体会,所以采用了特殊的方法在结构内完成了ORM,之于特殊在什么地方咱们先经过一些简略示例后再来阐明一下完成原理。

一、CRUD操作

  仍是用体系自带的Emploee模型作为示例,在IDE新建服务模型增加以下代码保存发布后可经过主菜单->Service->Invoke运转测验:

//业务新建两条记载
var emp1 = new Entities.Emploee();
emp1.Name = "Rick";
emp1.Account = "rick@appbox.dev";
emp1.Birthday = new DateTime(1977, 3, 16);
var emp2 = new Entities.Emploee();
emp2.Name = "Johne";
emp2.Account = "johne@appbox.dev"
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编译为动态服务组件。其间虚拟代码转化为运转时代码的进程首要是:
  1. 将实体特点取赋值操作(eg: entity.Name = "aa" 或 var temp = entity.Name)转化为针对运转时Entity类的GetXXX()及SetXXX()操作。**这儿需求留意的是运转时只存在一个Entity类(类似于KVO经过字典表保存特点值)**
  2. 将查询条件转化为可序列化的表达式,这样存储引擎履行查询指令时可托付clr emit生成过滤指令,存储引擎在扫描时直接运用过滤指令核算满意条件的记载。

运转时:

  这部分完成参阅以下流程图,需求留意的是存储引擎是依据RocksDB的,实体数据转化为KV数据(如Key=Id, Value=[字段标识:字段值;字段标识:字段值]),这样转化进程就不需求运用反射或Emit。

三、功能测验

  作者做了简略的功能测验(单节点I74C8G虚拟机):

  • 并发刺进不带索引不带外键的简略实体约28000tps;
  • 经过专一索引查询约80000qps;
  • 带条件扫描少数记载约35000qps。

四、查询约束

  • 存储引擎不支撑join,可经过别离查询出数据后使用Linq做join操作;
  • 存储引擎暂只支撑依据Entity.Id或指定索引查询排序,不支撑自界说排序。

五、本篇小结

  本篇首要介绍了结构集成的ORM的特殊完成,假如您有问题或Bug陈述,请留言或在[Github](https://github.com/enjoycode/appbox.deploy)提交Issue。

推荐阅读