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脚本:

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 数据访问引擎来说,性能的关注点主要(不限)有这些要素:

  1. 生成简洁高效的 SQL 脚本,并尽可能利用特定数据库的最新 SQL 语法;

  2. 数据查询结果的实体组装(Populate)过程必须高效;

  3. 避免反射,有效的语法树缓存。

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

其他

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

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

支持赞助

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

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

  1. 关注 Zongsoft 微信公众号,对我们的文章进行打赏;

  2. 加入 Zongsoft 知识星球圈,可以获得在线问答和技术支持;

  3. 如果您的企业需要现场技术支持与辅导,又或者需要开发新功能、即刻的错误修复等请发邮件给我。


提醒:点击左下角的“阅读原文”会有更好的阅读体验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312989.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 .NET Core模板引擎创建自定义的模板和项目

本文要点.NET CLI 包含了一个模板引擎&#xff0c;它可以直接利用命令行创建新项目和项目项。这就是“dotnet new”命令。默认模板集涵盖了默认控制台和基于 ASP.NET 的应用程序以及测试项目所需的基本项目和文件类型。自定义模板可以创建更加有趣或定制化的项目和项目项&#…

.NET Core前后端分离快速开发框架(Core.3.0+AntdVue)

时间真快&#xff0c;转眼今年又要过去了。回想今年&#xff0c;依次开源发布了Colder.Fx.Net.AdminLTE(254Star)、Colder.Fx.Core.AdminLTE(335Star)、DotNettySocket(82Star)、IdHelper(47Star)&#xff0c;这些框架及组件都是本着以实际出发&#xff0c;实事求是的态度&…

.Net开发3年,应聘大厂惨遭淘汰,如何翻身打脸面试官?

(设计师忘记了&#xff0c;这里还有个双十一福利课&#xff0c;还能1元秒杀&#xff01;)

面对金九银十铜十一你真的准备好了吗?

作者&#xff1a;回首笑人间&#xff0c;高级Java工程师一枚&#xff0c;热爱研究开源技术&#xff0c;架构师社区合伙人&#xff01;前言&#xff1a;又是一年一度的金九银十跳槽季&#xff0c;回首在经历了半个月的求职奔波后&#xff0c;终于又能安稳的静下心来敲代码了&…

深入理解二叉搜索树

什么是二叉搜索树&#xff1f; 顾名思义&#xff0c;一颗二叉搜索树是基于二叉树来组织的&#xff0c;它包括许多动态集合操作&#xff08;Search&#xff0c;MiniNum, MaxiNum, Prodecessor, Successor, Insert 和Delete等&#xff09;。二叉搜索树上的基本操作所花费的时间与…

深入理解堆(最大堆,最小堆及堆排序)

基本概念&#xff1a; 1、完全二叉树&#xff1a;若二叉树的深度为h&#xff0c;则除第h层外&#xff0c;其他层的结点全部达到最大值&#xff0c;且第h层的所有结点都集中在左子树。 2、满二叉树&#xff1a;满二叉树是一种特殊的的完全二叉树&#xff0c;所有层的结点都是最…

王炸吐血整理60个Redis面试题,全网最全了

1.Redis 是一个基于内存的高性能key-value数据库。 2.Redis相比memcached有哪些优势&#xff1a; memcached所有的值均是简单的字符串&#xff0c;redis作为其替代者&#xff0c;支持更为丰富的数据类型redis的速度比memcached快很多redis可以持久化其数据3.Redis是单线程 redi…

H.266 参考软件VTM下载和安装

1、下载安装cmake &#xff0c;下载地址https://cmake.org/。 安装后打开控制面板-系统-高级系统设置-环境变量-PATH-编辑-输入cmake.exe的路径即可。 2、如果你之前&#xff08;HEVC&#xff09;时已经下载好了SVN&#xff0c;直接在桌面点击右键SVNcheckout&#xff0c;出来…

VTM编码结构框架

VTM流程差不多就是这样子的啦&#xff0c;后续会继续补充&#xff0c;由于能力有限&#xff0c;如有错误&#xff0c;欢迎指正。在后面会进一步分析每一个函数&#xff0c;并分析流程的细节。

AspNetCore应用注意这一点,CTO会对你刮目相看

背景已经有很多文章记录了Web程序中采用异步编程的优势和.Net异步编程的用法&#xff0c; 异步编程虽然不能解决查询数据库的瓶颈&#xff0c; 但是利用线程切换&#xff0c;能最大限度的弹性利用工作线程&#xff0c; 提高了web服务的响应能力。????9012年了&#xff0c;再…

ASP.NET Core如何限制请求频率

ASP.NET Core如何限制请求频率&#xff0c;为了防止恶意请求&#xff0c;我们往往会对接口请求的频率做限制&#xff0c;比如请求间隔&#xff0c;一段时间内请求的次数&#xff0c;针对部分IP做出不同的限制策略如何去限制请求频率不需要我们去实现&#xff0c;用上AspNetCore…

从AppDomain迁移到AssemblyLoadContext

AssemblyLoadContext基本上AssemblyLoadContext是AppDomain的继承者&#xff0c;它提供相同而且更多的功能-除了安全边界&#xff08;隔离&#xff09;。最小的安全边界是进程&#xff0c;因此你将需要使用进程间通信来正确隔离数据和代码执行。官网文档中提到Appdomain已经过时…

【.NET Core 3.0】小技巧 || 原生DI一对多注入

本文是一个技巧文章&#xff0c;内容很短&#xff0c;但是被提问的频率很高&#xff0c;所以记录下来&#xff0c;以待大家不时之需。以下的代码&#xff0c;是通过原生的依赖注入来讲解的&#xff0c;其他的第三方框架&#xff0c;可以自己自定义扩展&#xff0c;效果是一样的…

大话数据结构学习笔记二:算法

一 算法定义 算法是解决特定问题求解步骤的描述&#xff0c;在计算机中表现为指令的有限序列&#xff0c;并且每条指令表示一个或多个操作。 二 算法的特性&#xff1a; 1 输入输出&#xff1a;算法具有零个或者多个输入&#xff0c;至少有一个或者多个输出。 2 有穷性&…

ASP.NET Core 3.0 gRPC 身份认证和授权

一.开头聊骚本文算是对于 ASP.NET Core 3.0 gRPC 研究性学习的最后一篇了&#xff0c;以后在实际使用中&#xff0c;可能会发一些经验之文。本文主要讲 ASP.NET Core 本身的认证授权和gRPC接入&#xff0c;认证方式采用目前主流的 JWT 结合 IdentityServer4。二.服务端配置我们…

ASP.NET Core中使用MediatR实现命令和中介者模式

作者&#xff1a;依乐祝原文地址&#xff1a;https://www.cnblogs.com/yilezhu/p/9866068.html在本文中&#xff0c;我将解释命令模式&#xff0c;以及如何利用基于命令模式的第三方库来实现它们&#xff0c;以及如何在ASP.NET Core中使用它来解决我们的问题并使代码简洁。因此…

使用ASP.NET Core 3.x 构建 RESTful API - 3.2 路由

路由机制会把一个请求的URI映射到一个Controller上面的Action&#xff0c;所以当你发送一个HTTP请求的时候&#xff0c;MVC框架会解析这个请求的URI&#xff0c;并尝试着把它映射到一个Controller上面的Action。两个路由中间件在ASP.NET Core 3.x里面&#xff0c;建议使用Endpo…

EFCore批量操作,你真的清楚吗

背景EntityFramework Core有许多新的特性&#xff0c;其中一个重要特性便是批量操作。批量操作意味着不需要为每次Insert/Update/Delete操作发送单独的命令&#xff0c;而是在一次SQL请求中发送批量组合指令。EFCore批量操作实践批处理是期待已久的功能&#xff0c;社区多次提出…

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

asp.net core 自定义 Policy 替换 AllowAnonymous 的行为Intro最近对我们的服务进行了改造&#xff0c;原本内部服务在内部可以匿名调用&#xff0c;现在增加了限制&#xff0c;通过 identity server 来管理 api 和 client&#xff0c;网关和需要访问api的客户端或api服务相互调…

前端小白在asp.net core mvc中使用ECharts

对于在浏览器中绘制图形图表&#xff0c;目前有较多的js类库可以使用&#xff0c;如&#xff1a;ChartJS&#xff0c;Flot&#xff0c;canvasjs等&#xff0c;但是今天介绍的主角为国产图表库&#xff0c;并在apache孵化&#xff0c;就是大名鼎鼎的echarts。前方高能【官方介绍…