一个类GraphQL的ORM数据访问框架发布
Zongsoft.Data 发布公告
很高兴我们的 ORM 数据访问框架(Zongsoft.Data)在历经两个 SaaS 产品的应用之后,今天正式宣布对外推广!
这是一个类 GraphQL 风格的 ORM(Object/Relational Mapping) 数据访问框架。
又一个轮子?
在很长时间里,.NET 阵营似乎一直缺乏一个被普遍使用的 ORM 数据访问框架,从最早的原生 ADO.NET 到舶来品 iBatis.NET 和 Hibernate.NET,后来又经历了 Linq for SQL 与 Entity Framework 的混战,可能是因为 Entity Framework 早期版本的模糊定位和反复变更的设计导致了它失之霸主之位,进而造就了一段百舸争流、群雄共逐的战国时代。在历经漫长而反复的期待、失望、纠结和痛苦之后,我终于决定动手造一个轮子。
设计理念
在开始动手之前,先确定以下基本设计原则:
- 数据库优先(Database First)
- 严格的 POCO/POJO 支持
- 映射模型与代码完全隔离
- 禁止业务层出现 SQL 和类 SQL 代码
在一个业务系统中,数据结构及其关系毋庸置疑是最底层的基础性结构,数据库应由系统架构师或开发负责人进行仔细设计 (No Schema/Weakly Schema 的思潮是涂抹了蜂蜜的毒药),数据访问映射以数据库表结构关系为基石,在此之上业务层亦以概念映射模型为准绳,层级之间相互隔离。
领域模型实体避免通过注解 (标签) 来进行元数据定义,应确保严格符合 POCO/POJO 范式。通过语义化的 Schema 来声明访问的数据结构关系,禁止应用层的 SQL 和 Linq 式的类 SQL 代码可降低业务层对数据层的依赖、提升代码可维护性外,还具备更加统一可控的便利性,并为数据访问引擎的实现提供了更大的优化空间和自由度。
范例说明
下面通过三个的例子 (注:例子均基于 Zongsoft.Community 项目) 来佐证上面的部分设计理念,更多示例和阐述请参考 Zongsoft.Data 项目的 README.md 文档和 Zongsoft.Community 项目的代码。
提示: 下面的范例均基于 Zongsoft.Community 开源项目,该项目是一个完整的论坛社区的后台程序。你可能需要预先阅读一下该项目的《数据库表结构设计》文档,以便更好的理解范例代码的业务逻辑。
示例一
导航查询及导航过滤
var forums = this.DataAccess.Select<Forum>(
Condition.Equal("SiteId", this.User.SiteId) &
Condition.In("Visibility", Visibility.Internal, Visibility.Public) |
(
Condition.Equal("Visibility", Visibility.Specified) &
Condition.Exists("Users",
Condition.Equal("UserId", this.User.UserId) &
(
Condition.Equal("IsModerator", true) |
Condition.NotEqual("Permission", Permission.None)
)
)
),
"*, MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}}"
);
上述数据访问的查询方法大致生成如下SQL脚本:
SELECT
t.*,
t1.ThreadId AS 'MostRecentThread.ThreadId',
t1.Title AS 'MostRecentThread.Title',
t1.CreatorId AS 'MostRecentThread.CreatorId',
t2.UserId AS 'MostRecentThread.Creator.UserId',
t2.Name AS 'MostRecentThread.Creator.Name',
t2.Nickname AS 'MostRecentThread.Creator.Nickname',
t2.Avatar AS 'MostRecentThread.Creator.Avatar'
FROM Forum t
LEFT JOIN Thread AS t1 ON
t.MostRecentThreadId=t1.ThreadId
LEFT JOIN UserProfile AS t2 ON
t1.CreatorId=t2.UserId
WHERE
t.SiteId = @p1 AND
t.Visibility IN (@p2, @p3) OR
(
t.Visibility = @p4 AND
EXISTS
(
SELECT u.SiteId, u.ForumId, u.UserId
FROM ForumUser u
WHERE u.SiteId = t.SiteId AND
u.ForumId = t.ForumId AND
u.UserId = @p5 AND
(
u.IsModerator = @p6 OR
u.Permission != @p7
)
)
);
上述示例通过
Select查询方法的schema参数 (即值为*, MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}}的参数) 从数据结构关系的层次指定了查询数据的形状,因而不再需要 SQL 或类 SQL 语法中 JOIN 这样命令式的语法元素,它不光提供了更简洁且语义化的 API 访问方式,而且还给数据访问引擎底层提供了更大的优化空间和自由度。如果将
Select查询方法的schema参数值改为*,Moderators{*},MostRecentThread{ThreadId,Title,Creator{Name,Nickname,Avatar}}后,数据访问引擎会将查询内部分解为一对多的两条 SQL 语句进行迭代执行,而这些都不需要业务层进行分拆处理,因而提升了效率并降低了业务层的复杂度。注: 将 Schema 模式表达式通过 Web API 提供给前端应用,将大大减少后端开发的工作量,提升前后端的工作效率。
示例二
一对多的关联新增
// 构建待新增的实体对象
var forum = new
{
SiteId = this.User.SiteId,
GroupId = 100,
Name = "xxxx",
// 一对多的导航属性
Users = new ForumUser[]
{
new ForumUser { UserId = 1001, IsModerator = true },
new ForumUser { UserId = 1002, Permission = Permission.Read },
new ForumUser { UserId = 1003, Permission = Permission.Write },
}
}
// 执行数据新增操作
this.DataAccess.Insert<Forum>(forum, "*, Users{*}");
上述数据访问的新增方法大致生成如下SQL脚本:
/* 主表插入语句,执行一次 */
INSERT INTO Forum (SiteId,ForumId,GroupId,Name,...) VALUES (@p1,@p2,@p3,@p4,...);
/* 子表插入语句,执行多次 */
INSERT INTO ForumUser (SiteId,ForumId,UserId,Permission,IsModerator) VALUES (@p1,@p2,@p3,@p4,@p5);
上述示例通过
Insert新增方法的schema参数(即值为*,User{*}的参数)指定了新增数据的形状,由数据访问引擎根据映射定义自动处理底层的 SQL 执行方式,确保业务层代码的简洁和更高的执行效率。
示例三
一对一和一对多的关联更新,对于“一对多”的导航属性,还能确保该属性值 (集合类型) 以 UPSERT 模式写入。
public bool Approve(ulong threadId)
{
//构建更新的条件
var criteria =
Condition.Equal(nameof(Thread.ThreadId), threadId) &
Condition.Equal(nameof(Thread.Approved), false) &
Condition.Equal(nameof(Thread.SiteId), this.User.SiteId) &
Condition.Exists("Forum.Users",
Condition.Equal(nameof(Forum.ForumUser.UserId), this.User.UserId) &
Condition.Equal(nameof(Forum.ForumUser.IsModerator), true));
//执行数据更新操作
return this.DataAccess.Update<Thread>(new
{
Approved = true,
ApprovedTime = DateTime.Now,
Post = new
{
Approved = true,
}
}, criteria, "*,Post{Approved}") > 0;
}
上述数据访问的更新方法大致生成如下SQL脚本:
/* 以下代码为支持 OUTPUT/RETURNING 子句的数据库(如:SQLServer,Oracle,PostgreSQL) */
/* 根据更新的关联键创建临时表 */
CREATE TABLE #TMP
(
PostId bigint NOT NULL
);
/* 更新主表,并将更新的关联键输出到内存临时表 */
UPDATE T SET
T.[Approved]=@p1,
T.[ApprovedTime]=@p2
OUTPUT DELETED.PostId INTO #TMP
FROM [Community_Thread] AS T
LEFT JOIN [Community_Forum] AS T1 ON /* Forum */
T1.[SiteId]=T.[SiteId] AND
T1.[ForumId]=T.[ForumId]
WHERE
T.[ThreadId]=@p3 AND
T.[Approved]=@p4 AND
T.[SiteId]=@p5 AND EXISTS (
SELECT [SiteId],[ForumId]
FROM [Community_ForumUser]
WHERE [SiteId]=T1.[SiteId] AND
[ForumId]=T1.[ForumId] AND
[UserId]=@p6 AND
[IsModerator]=@p7
);
/* 更新关联表 */
UPDATE T SET
T.[Approved]=@p1
FROM [Community_Post] AS T
WHERE EXISTS (
SELECT [PostId]
FROM #TMP
WHERE [PostId]=T.[PostId]);
上述示例通过
Update更新方法的schema参数(即值为*,Post{Approved}的参数)指定了更新数据的形状,数据访问引擎将根据数据库类型生成高效的 SQL 语句,对于业务层而言这一切都是无感的、透明的。对于一对多的导航属性,数据访问引擎默认将以 UPSERT 模式处理子集的写入,关于 UPSERT 更多信息请参考 Zongsoft.Data 项目文档。
性能
我们希望提供最佳的综合性价比,对于一个 ORM 数据访问引擎来说,性能的关注点主要 (不限) 有这些要素:
- 生成简洁高效的 SQL 脚本,并尽可能利用特定数据库的最新 SQL 语法;
- 数据查询结果的实体组装(Populate)过程必须高效;
- 避免反射,有效的语法树缓存。
实现层面我们采用 Emitting 动态编译技术对实体组装(Populate)、数据参数绑定等进行预热处理,可查阅 DataPopulator 等相关类的源码深入了解。
其他
得益于 “以声明方式来表达数据结构关系” 的语义化设计理念,相对于命令式设计而言,它使得程序意图更加聚焦,天然地对底层数据的表达和优化更加宽容与自由。
更多详细内容 (譬如:读写分离、继承表、数据模式、映射文件、过滤器、验证器、类型转换、数据隔离) 请查阅相关文档。
支持赞助
我们欢迎并期待任何形式的推广支持!
如果你认同我们的设计理念请为这个项目点赞(Star),如果你认为该项目很有用,并且希望支持它未来的发展,请给予必要的资金来支持它:
- 关注 Zongsoft 微信公众号,对我们的文章进行打赏;
- 加入 Zongsoft 知识星球圈,可以获得在线问答和技术支持;
- 如果您的企业需要现场技术支持与辅导,又或者需要开发新功能、即刻的错误修复等请发邮件给我。


提醒: 本文可能会更新,请阅读原文:http://zongsoft.com/blog/zh-cn/zongsoft/announcing-data-engine,以避免因内容陈旧而导致的谬误,同时亦有更好的阅读体验。
一个类GraphQL的ORM数据访问框架发布的更多相关文章
- 分享自己的超轻量级高性能ORM数据访问框架Deft
Deft 简介 Deft是一个超轻量级高性能O/R mapping数据访问框架,简单易用,几分钟即可上手. Deft包含如下但不限于此的特点: 1.按照Transact-SQL的语法语义风格来设计,只 ...
- 架构从最简单的数据访问框架(ORM)到资源调度和治理中心(SOA)说起
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构当网站流量很小时,只需一个应用,将 ...
- 超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上
超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上 阅读目录 Deft简介 Deft 核心类介绍 Deft 3分钟即可上手使用 其他可选的配置参数 性能测试 Demo代码下载 回到顶 ...
- 9.ORM数据访问
1.Spring对ORM的支持 ORM : 对象关系映射(Object Relational Mapping)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术基于ORM的数据持久层框架有: ...
- java使用netty模拟实现一个类dubbo的分布式服务调用框架
本文较长,如果想直接看代码可以查看项目源码地址: https://github.com/hetutu5238/rpc-demo.git 要想实现分布式服务调用框架,我们需要了解分布式服务一般需要的功能 ...
- FluentData,一个轻量级开源的.NET ORM数据持久化框架
FluentData:一种使用Fluent API的新型轻量级ORM模型 FluentData 是微型 ORM(micro-ORM)家族的一名新成员,旨在比大型 ORM(full ORM)更加易用. ...
- ORM数据层框架的设计热点:更新指定的列的几种设计方案
ORM框架的定义:对象-关系映射(Object/Relation Mapping,简称ORM) 常见的是:数据库结构=>映射Object(实体属性)=>基于实体类的操作. 还有一种:数据库 ...
- .NET中微软实体框架的数据访问方法
介绍 本文的目的是解释微软的实体框架提供的三种数据访问方法.网上有好几篇关于这个话题的好文章,但是我想以一个教程的形式更详细地介绍这个话题,这个教程对于开始学习实体框架及其方法的人来说是个入门.我们将 ...
- JAVAWEB基础模块开发顺序与数据访问对象实现类步骤
一.模块的开发的顺序 1. 定义数据表 2. 新建模型类 3. 新建"add.jsp" 4. 实现AddServlet中的doGet()方法 5. 定义Dao.Service接口 ...
随机推荐
- 代码审计-凡诺CMS 2.1文件包含漏洞
0x01代码审计 后台账号密码: admin admin 安装好了是这样的 漏洞文件:/channel.php if (ism()) { include($dir.$t_mpath.$c_mcmode ...
- MySQL常用sql语句-----数据库操作
在数据库操作中,操作基本都是围绕增删改查来操作.简称CRUD C创建创建 R读取/检索查询 U Update修改 D删除删除 在数操作数据库时,所有的数据库语句都要以分号结束 数据库操作不区分大小写 ...
- 微信小程序初级教程
小程序代码构成 JSON 配置 WXML 模版 WXSS 样式 JS 逻辑交互 JSON 配置 在小程序中,JSON扮演的静态配置的角色. 小程序配置 app.json { "pages&q ...
- 百万年薪python之路 -- 递归
递归(每当有一个函数被递归调用,就应该要有一个返回值,才能正常把递归的返回值'归'回来) 一个正经的递归: 1.不断调用自己本身 2.有明确的结束条件 递归注重于"一递 一归&quo ...
- 闪讯 开启wifi教程
这是我自己试了几次之后发现的,也不是什么技术活. 首先说下,我的比较是小米pro笔记本,一般笔记本都是自带wifi功能的.如果要开wifi的话,必须是用网线连接才可以,通过wifi连接网络就不能开移动 ...
- 使用SQLserver Management Studio连接VS2012自带数据库
下载 Microsoft® SQL Server® 2008 Management Studio Express http://www.microsoft.com/zh-CN/download/det ...
- Java基础(三十二)JDBC(2)连接数据库
一.连接数据库的过程 连接数据库的过程:加载数据库驱动程序,不过只需在第一次访问数据库时加载一次,然后在每次访问数据库时创建一个Connection实例,然后执行操作数据库的SQL语句,并返回执行结果 ...
- 设置和获取html里面的内容.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 23 个重难点突破,带你吃透 Service 知识点「长达 1W+ 字」
前言 学 Android 有一段时间了,想必不少人也和我一样,平时经常东学西凑,感觉知识点有些凌乱难成体系.所以趁着这几天忙里偷闲,把学的东西归纳下,捋捋思路. 这篇文章主要针对 Service 相关 ...
- 你不知道的Canvas(二)
你不知道的Canvas(二) 一.色彩Colors 到目前为止,我们只看到过绘制内容的方法.如果我们想要给图形上色,有两个重要的属性可以做到:fillStyle 和 strokeStyle. fill ...