Zongsoft.Data 发布公告

很高兴我们的 ORM 数据访问框架(Zongsoft.Data)在历经两个 SaaS 产品的应用之后,今天正式宣布对外推广!
这是一个类 GraphQL 风格的 ORM(Object/Relational Mapping) 数据访问框架。

又一个轮子?

在很长时间里,.NET 阵营似乎一直缺乏一个被普遍使用的 ORM 数据访问框架,从最早的原生 ADO.NET 到舶来品 iBatis.NETHibernate.NET,后来又经历了 Linq for SQL 与 Entity Framework 的混战,可能是因为 Entity Framework 早期版本的模糊定位和反复变更的设计导致了它失之霸主之位,进而造就了一段百舸争流、群雄共逐的战国时代。在历经漫长而反复的期待、失望、纠结和痛苦之后,我终于决定动手造一个轮子。

设计理念

在开始动手之前,先确定以下基本设计原则:

  • 数据库优先(Database First)
  • 严格的 POCO/POJO 支持
  • 映射模型与代码完全隔离
  • 禁止业务层出现 SQL 和类 SQL 代码

在一个业务系统中,数据结构及其关系毋庸置疑是最底层的基础性结构,数据库应由系统架构师或开发负责人进行仔细设计 No Schema/Weakly Schema 的思潮是涂抹了蜂蜜的毒药),数据访问映射以数据库表结构关系为基石,在此之上业务层亦以概念映射模型为准绳,层级之间相互隔离。

领域模型实体避免通过注解 (标签) 来进行元数据定义,应确保严格符合 POCO/POJO 范式。通过语义化的 Schema 来声明访问的数据结构关系,禁止应用层的 SQLLinq 式的类 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脚本:

SELECTt.*,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 tLEFT JOIN Thread AS t1 ONt.MostRecentThreadId=t1.ThreadIdLEFT JOIN UserProfile AS t2 ONt1.CreatorId=t2.UserId
WHEREt.SiteId = @p1 ANDt.Visibility IN (@p2, @p3) OR(t.Visibility = @p4 ANDEXISTS(SELECT u.SiteId, u.ForumId, u.UserIdFROM ForumUser uWHERE u.SiteId = t.SiteId ANDu.ForumId = t.ForumId ANDu.UserId = @p5 AND(u.IsModerator = @p6 ORu.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 SETT.[Approved]=@p1,T.[ApprovedTime]=@p2
OUTPUT DELETED.PostId INTO #TMP
FROM [Community_Thread] AS TLEFT JOIN [Community_Forum] AS T1 ON /* Forum */T1.[SiteId]=T.[SiteId] ANDT1.[ForumId]=T.[ForumId]
WHERET.[ThreadId]=@p3 ANDT.[Approved]=@p4 ANDT.[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 SETT.[Approved]=@p1
FROM [Community_Post] AS T
WHERE EXISTS (SELECT [PostId]FROM #TMPWHERE [PostId]=T.[PostId]);

上述示例通过 Update 更新方法的 schema 参数(即值为 *,Post{Approved} 的参数)指定了更新数据的形状,数据访问引擎将根据数据库类型生成高效的 SQL 语句,对于业务层而言这一切都是无感的、透明的。

对于一对多的导航属性,数据访问引擎默认将以 UPSERT 模式处理子集的写入,关于 UPSERT 更多信息请参考 Zongsoft.Data 项目文档。

性能

我们希望提供最佳的综合性价比,对于一个 ORM 数据访问引擎来说,性能的关注点主要 (不限) 有这些要素:

  1. 生成简洁高效的 SQL 脚本,并尽可能利用特定数据库的最新 SQL 语法;
  2. 数据查询结果的实体组装(Populate)过程必须高效;
  3. 避免反射,有效的语法树缓存。

实现层面我们采用 Emitting 动态编译技术对实体组装(Populate)、数据参数绑定等进行预热处理,可查阅 DataPopulator 等相关类的源码深入了解。

其他

得益于 “以声明方式来表达数据结构关系” 的语义化设计理念,相对于命令式设计而言,它使得程序意图更加聚焦,天然地对底层数据的表达和优化更加宽容与自由。

更多详细内容 (譬如:读写分离、继承表、数据模式、映射文件、过滤器、验证器、类型转换、数据隔离) 请查阅相关文档。

支持赞助

我们欢迎并期待任何形式的推广支持!

如果你认同我们的设计理念请为这个项目点赞(Star),如果你认为该项目很有用,并且希望支持它未来的发展,请给予必要的资金来支持它:

  1. 关注 Zongsoft 微信公众号,对我们的文章进行打赏;
  2. 加入 Zongsoft 知识星球圈,可以获得在线问答和技术支持;
  3. 如果您的企业需要现场技术支持与辅导,又或者需要开发新功能、即刻的错误修复等请发邮件给我。


提醒: 本文可能会更新,请阅读原文:http://zongsoft.com/blog/zh-cn/zongsoft/announcing-data-engine,以避免因内容陈旧而导致的谬误,同时亦有更好的阅读体验。

一个类GraphQL的ORM数据访问框架发布相关推荐

  1. DataRabbit 轻量的数据访问框架(13)--DataRabbit 3.0 ORM性能大幅度提升!

       DataRabbit 3.0重写了DataRabbit 2.0的ORM实现的内核,性能提升了90倍左右,结果是DataRabbit 3.0的ORM性能与直接使用ADO.NET的性能已经非常接近. ...

  2. ORM数据层框架的设计热点:更新指定的列的几种设计方案

    ORM框架的定义:对象-关系映射(Object/Relation Mapping,简称ORM) 常见的是:数据库结构=>映射Object(实体属性)=>基于实体类的操作. 还有一种:数据库 ...

  3. 基于存储过程的可扩展性数据访问框架

    步骤一. Web.config <connectionStrings> <add name="conn" connectionString="Data ...

  4. 请编写一个类,该类能够实现访问一个web应用下的Servlet后,还能在浏览器地址栏中显示出同站点下的index.jsp的路径

    朋友我知道你很迷茫,不知道这是干啥的,要回答啥,哼哼-! 就是一个response的重定向,老师给的题目真是扰乱人 ①创建jsp <%@ page language="java&quo ...

  5. junit编写测试代码_编写数据访问代码测试-不测试框架

    junit编写测试代码 当我们向数据访问代码编写测试时,是否应该测试其公共API的每种方法? 一开始听起来很自然. 毕竟,如果我们不测试所有内容,那么如何知道我们的代码可以按预期工作? 这个问题为我们 ...

  6. Spring Boot中使用Spring-data-jpa让数据访问更简单、更优雅

    在上一篇Spring中使用JdbcTemplate访问数据库 中介绍了一种基本的数据访问方式,结合构建RESTful API和使用Thymeleaf模板引擎渲染Web视图的内容就已经可以完成App服务 ...

  7. 转载:使用Spring进行数据访问(Data Access With Spring)

    Table of Contents 1.1. 统一的数据访问异常层次体系(Consistent Exception Hierarchy In Spring) 1.1.1. DAO模式的背景(Backg ...

  8. DNN 数据访问策略 (转)

    经过几天断断续续的努力,这篇文章终于翻译结束,文章主要讲了DNN的数据访问策略,对于了解系统整体上是如何工作的有一定的帮助,希望能给dnn的初学者一些有用的信息.由于翻译的匆忙+水平有限,错误或不当之 ...

  9. Spring Boot - 构建数据访问层

    文章目录 基础规范: JDBC 关系型数据库访问规范 JDBC 规范中的核心编程对象 DriverManager DataSource Connection Statement/PreparedSta ...

最新文章

  1. Math.Pow()是如何在.NET Framework中实现的?
  2. IMOAutocompletionViewController
  3. 常见的加密和解密算法—MD5
  4. LiveWriter测试
  5. W ndoWs文件夹窗口,如何在本地网络中访问-Synology-NAS-上的文件-(Wndows).pdf
  6. java中拷贝文件的代码_拷贝文件夹中的所有文件到另外一个文件夹
  7. h5分享到朋友圈_微信朋友圈创意招聘广告制作方法 H5模板
  8. Golang生成C动态库.so和静态库.a
  9. 从矩阵谱分解到矩形的最少正方形剖分
  10. 谈谈网络协议,常见的网络协议有那些?
  11. c语言中整形的最大最小值,C语言编写程序输出10个整数中最小值或最大值
  12. 近视眼手术之后计算机专业,做完近视激光手术后多久可以整天对着电脑工作。应注意什么?...
  13. 计算机音乐按键有声音,计算器上的声音键是哪个键
  14. 301. 删除无效的括号【我亦无他唯手熟尔】
  15. 怎样进行两台电脑之间的数据传输?
  16. 转:Processing 编程学习指南
  17. 梅姨眼中最爱读英国书籍的人竟然是TA?
  18. 奥的斯电梯服务器自动呼梯,奥的斯MCS系统电梯轿厢内呼梯保密设置
  19. 转自Master HaKu 的C#调用C++写的Dll时的运行时错误解决
  20. 三星a5000刷Android原生,Samsung-A5000安卓5.0刷机包

热门文章

  1. Ubuntu16.04开机进入单用户模式,破解root密码
  2. 程序员必备软技能之科技趋势(一)
  3. k8s—centos7安装部署NFS服务器和客户端及基于nfs的动态存储storageclass使用总结
  4. 03 实现不同基本数据类型之间的类型转换 0214
  5. 前端开发 图片的插入 。。。。需演练
  6. javascript-流程控制-循环-分支-三元运算符
  7. redis查询key的数量
  8. TIDB报错statement count 5001 exceeds the transaction limitation, autocommit = false问题解决
  9. SpringBoot(十一)-- 动态数据源
  10. 私有云为先 ZStack还在谋划一个更大的混合云世界