.Net Core ORM 框架

article/2025/10/19 7:43:03

我的.net core orm 框架

  • 一个简单的orm框架
    • 支持的数据库
    • 版本
    • 新的版本
    • 项目地址
    • 使用方式
    • 实现方式
    • 高级特性
    • 扩展函数
    • 性能
    • bug

一个简单的orm框架

作者在使用很多orm框架的时候觉得查询语句写法似乎不是很好用,例如sqlSugar,EF,sqlSugar呢链接的时候必须注意表的别称,而且多条件拼接似乎也不是很友好,而ef呢多表连接查询是非常不友好的,这个简单的orm呢查询语句写起来非常优雅,不信你可以试一试。

支持的数据库

mysql,sqlservice,sqlite
后续会加其他数据库,提供了扩展的方式

版本

.net6

新的版本

.NET Standard 2

项目地址

GitHub 项目地址:https://github.com/soulzdm/NetCore-ORM-Simple

使用方式

作者的经验并不是非常丰富,而且是第一个版本,因此可能存在诸多问题,不建议直接使用生产环境,建议测试,了解一下再使用。如果使用过程中遇到问题欢迎提问题。一些高级分库分表的功能还暂时不支持,可以不用配置,后续会更新。
工具已经被做成程序包了可以使用nuget下载。
搜素关键词Simple.ORM

创建一个数据库连接

// 创建一个客户端 老版本ISimpleClient simpleClient = new SimpleClient(new DataBaseConfiguration(false,new ConnectionEntity("数据库连接字符串"){IsAutoClose = true,//是否自动释放链接DBType = eDBType.Mysql,数据类型目前仅仅支持Name = "test1",连接名称ReadWeight = 5,//读的权重-不是多个数据库可以不用配置WriteReadType = eWriteOrReadType.ReadOrWrite//配置数据库支持读和写入}));
///新的版本
//需要手动引入相关数据的连接驱动
//以mysql为例子MySql.Data
//相对来说配置的步骤多了一点
//step 1.DataBaseConfiguration.DBDrives.Add(eDBType.Mysql, Tuple.Create(typeof(MySqlConnection), typeof(MySqlParameter)));
//step 2.client = new SimpleClient(new DataBaseConfiguration(true,new(MastStr){IsAutoClose = true,DBType = eDBType.Mysql,Name = "master",ReadWeight=1,WriteReadType = eWriteOrReadType.ReadOrWrite} ));

测试用的实体

 [ClassName("dictionarytable")]//对应数据库的表名称public class DictionEntity{[Key(true)]public int ID { get; set; }public int MainID { get; set; }public int RowID { get; set; }public string RowName { get; set; }public string RowDesc { get; set; }}[ClassName("UserTable")]public class UserEntity{[Key(true)]public int ID { get; set; }public int UserName { get; set; }public int CompanyId { get; set; }}[ClassName("CompanyTable")]public class CompanyEntity{[Key(true)]public int ID { get; set; }public int CompanyName { get; set; }}

添加

 simpleClient.Insert(new DictionEntity());

更新

var entity=simpleClient.Queryable<DictionEntity>().FirstOrDefault();entity.RowName = "testUpdate";simpleClient.Update(entity);

查询

//单表查询var entity = simpleClient.Queryable<DictionEntity>().Select(d=>new{Name=d.RowName,Id=d.ID}).FirstOrDefault();var entity1 = simpleClient.Queryable<DictionEntity>().Select(d=>new{Name=d.RowName,Id=d.ID}).Where(d=>d.Name.Equals("test")).FirstOrDefault();//多表连接查询 无需关注参数的别称,只需要记住顺序即可var data1 = simpleClient.Queryable<UserEntity, CompanyEntity>((u,c)=>new JoinInfoEntity(new JoinMapEntity(eJoinType.Inner,u.CompanyId==c.ID))).FirstOrDefault();

实现方式

  • 实现思路
    生成sql语句中最过于麻烦的在于查询语句,有表与表的链接,分组,排序还有一些函数。.net core 提供了表达式目录树(Expression)该对象内部可以放置一个委托。官方呢也非常贴心提供了表达式目录树的解析相关类库expressionVisitor
    使用该类库呢可以将例如查询语句中的条件二元表达式解析,映射规则(select 部分),以及表与表的链接关系。将条件,连接还有映射部分解析完成后再将这些信息存放到相应类中,直到最后需要返回查询数据的时候根据不同数据库生成不同的语句,因为不同数据库的某些语句可能存在查询这样做呢可以屏蔽这种查询。当然最上层的一些接口的话其实和ef 和sqlsugar 非常相似,但是有些地方做了改进,改成了符合我自己的习惯。
    一.其中呢有些地方用到了动态类型,它好像可以跨过编译检查在有些地方不得不用例如存放(有时候使用者可能直接返回匿名对象但是不同通过反射构造,表达式内部的委托执行后可以返回匿名对象,但是这个委托的存放不太好声明变量存储,因此用动态类型,但是应当注意类型安全问题)
    二.在表达式目录树种取出变量的值也不是一件看似简单的类型,默认呢会将这一局部的所有变量保存放在一个匿名对象中,第一步是从该匿名对象中取出值,然而这还有吗结束,应为你可能是用了该变量的某个属性因此还得再通过反射取值。如果是数组字典集合又会有差异。

  • 遇到的问题
    在分组之后返回匿名对象目前是不支持的,因为分组之后又可以调用一些函数例如(原型:Sum(Expression<Func<TResult, TField>> expression) 实际使用还是便于看懂:Count=v.Count()),然后我前面实现构造匿名对象的方式是将最后一个具体的对象(最后一个具体的对象指的是select 中的最后一个具体的对象)构造出来然后使用表达式目录树中保存的委托将该对象放入内部然后执行返回匿名对象,如果内部调用了方法方法的参数又是一个委托似乎就有点麻烦事了;目前暂时还没有解决

 var JoinData2 = client.Queryable<UserEntity, RoleEntity, CompanyEntity>((u, r, c) => new JoinInfoEntity(new JoinMapEntity(eJoinType.Inner, u.RoleId.Equals(r.Id)),new JoinMapEntity(eJoinType.Inner, u.CompanyId.Equals(c.Id)))).Where((u, r, c) => u.Id > 10).Select((u,r,c)=>new ViewEntity {UserName=u.Name,CompanyId=c.Id,CompanyName=c.CompanyName,RoleId=r.Id}).GroupBy(v=>v.RoleId).Select((v) =>new GroupEntity(){Count=v.Count(),FirstOrDefaultName=v.FirstOrDefault(s=>s.UserName),Max=v.Max(s=>s.RoleId)}).ToList();

高级特性

  • 支持定义特性映射表名称和列名称(需要继承相关接口)
public class MyColumnAttrbute :AbsAttribute, IColumn{public MyColumnAttrbute(string name=null,bool key=false,bool autoIncrease=false,bool ignore=false) :base(name){Key= key;AutoIncrease= autoIncrease;Ignore= ignore;}public bool Key { get{ return key; } set { key = value; } }public bool AutoIncrease { get{ return autoIncrease; } set { autoIncrease = value; } }public bool Ignore { get { return ignore; } set { ignore = value; } }private bool key;private bool autoIncrease;private bool ignore;}public class MyTableAttrbute : AbsAttribute,IName{public MyTableAttrbute(string name = null) : base(name){}}
## 读写分离
1.配置一个写库多个从库从库
从库根据配置的权重进行均衡
```csharp
client = new SimpleClient(new DataBaseConfiguration(true,new ConnectionEntity(MastStr){IsAutoClose = true,DBType = eDBType.Mysql,Name = "master",WriteReadType = eWriteOrReadType.Write}, new (db1){IsAutoClose = true,DBType = eDBType.Mysql,Name = "db1",ReadWeight = 1,WriteReadType = eWriteOrReadType.Read},new(db2){IsAutoClose = true,DBType = eDBType.Mysql,Name = "db2",ReadWeight = 3,WriteReadType = eWriteOrReadType.Read},new(db3){IsAutoClose = true,DBType = eDBType.Mysql,Name = "db3",ReadWeight = 6,WriteReadType = eWriteOrReadType.Read}));client.SetAOPLog((sql, Params) =>{Console.WriteLine(sql);});client.SetAttr(typeof(MyTableAttrbute), typeof(MyColumnAttrbute));// var data=client.Queryable<MatchLog>().Take(10).ToList();}2.测试代码var master = 0;var db1 = 0;var db2 = 0;var db3 = 0;UserEntity iuser = new UserEntity();iuser.Name = "插入小明";iuser = client.Insert(iuser).ReturnEntity();iuser.Name = "更新小明";var result = client.Update(iuser).SaveChange();for (int i = 0; i < 100; i++){var user=client.Queryable<UserEntity>().Where(u=>u.Id.Equals(1)).First();if (user!=null){if (user.RoleId==1){db1++;}else if (user.RoleId==2){db2++;}else if (user.RoleId == 3){db3++;}else if (user.RoleId == 0){master++;}}}Console.WriteLine($"db1={db1}\n db2={db2}\n db3={db3} \n master={master}");

扩展函数

public static bool Contains<T>(T[]datas,T targe);/// <summary>/// 左边模糊查询/// </summary>/// <param name="value"></param>/// <param name="left"></param>/// <returns></returns>public static bool LeftContains(string value, string left);/// <summary>/// 右边模糊查询/// </summary>/// <param name="value"></param>/// <param name="left"></param>/// <returns></returns>public static bool RightContains(string value, string right);/// <summary>/// 日期函数/// </summary>/// <param name="start"></param>/// <param name="end"></param>/// <param name="type"></param>/// <returns></returns>public static int DateDiff(DateTime start,DateTime end,eDateType type);/// <summary>/// 日期函数/// </summary>/// <param name="start"></param>/// <param name="end"></param>/// <param name="type"></param>/// <returns></returns>public static int DateDiff(TimeSpan start, TimeSpan end, eDateType type);/// <summary>/// /// </summary>/// <param name="time"></param>/// <returns></returns>public static int Year(DateTime time);public static int Month(DateTime time);public static int Day(DateTime time);public static int Hour(DateTime time);public static int Second(DateTime time);public static DateTime Now();/// <summary>/// 向上取整数/// </summary>/// <param name="value"></param>/// <param name="len"></param>/// <returns></returns>public static float Round(float value,int len);public static double Round(double value, int len);public static decimal Round( decimal value, int len);/// <summary>/// 向下取整/// </summary>/// <param name="value"></param>/// <param name="len"></param>/// <returns></returns>public static float Truncate( float value, int len);public static double Truncate( double value, int len);public static decimal Truncate( decimal value, int len);public static bool IsNullOrEmpty( string strValue);public static bool IsNull<T>(T t);

性能

我自己简单测试了单表查询主要事件耗费在打开连接和command读取数据大约占用了百分之九十五以上的时间拼接sql和映射数据占用的时间非常少(以二十条而基准进行测试),单次查询一百毫秒左右映射数据生成sql只用了几毫秒,因此性能还是可以的。
单条语句查询一百次测试

Console.WriteLine("*****************Simple***************");Stopwatch watch = new Stopwatch();watch.Start();ISimpleClient client = new SimpleClient(new DataBaseConfiguration(false,new ConnectionEntity(strConnection){DBType=eDBType.Mysql,IsAutoClose=false,Name="Test"}));client.SetAPOLog((sql, pars) =>{//Console.WriteLine($"Date:{DateTime.Now}\t \t sql:{sql}");});int DataLength = 20;int count=100;long l = 0;long sum = 0;long sum2 = 0;long l2 = 0;for (int i = 0; i < count; i++){l = watch.ElapsedMilliseconds;var data = client.Queryable<MissionDetailEntity>().Where(m => !m.IsDelete || (m.EndTime < DateTime.Now && m.StartTime > DateTime.MinValue)).Take(DataLength);//Console.WriteLine(i);sum += watch.ElapsedMilliseconds-l;l2=watch.ElapsedMilliseconds;data.ToList();sum2+=watch.ElapsedMilliseconds-l2;}watch.Stop();Console.WriteLine($"耗时{watch.ElapsedMilliseconds}ms");Console.WriteLine($"平均每次耗时:{(watch.ElapsedMilliseconds+0.0) / count}ms");Console.WriteLine($"拼接语句耗时{sum}ms");Console.WriteLine($"拼接语句平均每次耗时:{(sum+0.0)/ count}ms");Console.WriteLine($"读取数据耗时{sum2}ms");Console.WriteLine($"读取数据平均每次耗时:{(sum2+0.0) / count}ms");

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以测试结果来看的话性能还是非常好的

bug

欢迎发现bug


http://chatgpt.dhexx.cn/article/PXIfphuu.shtml

相关文章

SQLAlchemy ORM框架

ORM简介 ORM 全称 Object Relational Mapping, 叫对象关系映射。简单的说&#xff0c;ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系。这样&#xff0c;我们要操作数据库&#xff0c;数据库中的表或者表中的一条记录就可以直接通过操作类或者类实例来完成。 对于…

ORM框架

ORM框架 一、什么是ORM框架 对象关系映射&#xff08;Object Relational Mapping&#xff0c;简称ORM&#xff09;模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术 ORM框架是连接数据库的桥梁&#xff0c;只要提供了持久化类与表的映射关系&#xff0c;OR…

ORM框架使用优缺点

1. 什么是ORM? 对象-关系映射&#xff08;Object-Relational Mapping&#xff0c;简称ORM&#xff09;&#xff0c;面向对象的开发方法是当今企业级应用开发环境中的主流开发方法&#xff0c;关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业…

Redis I/O 多路复用

引出IO多路复用 为什么 Redis 中要使用 I/O 多路复用这种技术呢&#xff1f; 首先&#xff0c;Redis 是跑在单线程中的&#xff0c;所有的操作都是按照顺序线性执行的&#xff0c;但是由于读写操作等待用户输入或输出都是阻塞的&#xff0c;所以 I/O 操作在一般情况下往往不能…

IO多路复用—由Redis的IO多路复用yinch

linux IO多路复用有epoll&#xff0c; poll, select&#xff0c;epoll性能比其他几者要好。 名词比较绕口&#xff0c;理解涵义就好。一个epoll场景&#xff1a;一个酒吧服务员&#xff08;一个线程&#xff09;&#xff0c;前面趴了一群醉汉&#xff0c;突然一个吼一声“倒酒”…

什么是IO多路复用?用来解决什么问题?如何实现?

白话IO多路复用 这里引述知乎大佬对于IO多路复用的机场空管的比喻和理解&#xff1a; 假设你是一个机场的空管&#xff0c; 你需要管理到你机场的所有的航线&#xff0c; 包括进港&#xff0c;出港&#xff0c; 有些航班需要放到停机坪等待&#xff0c;有些航班需要去登机口接…

I/O多路复用

https://blog.csdn.net/baixiaoshi/article/details/48708347 https://blog.csdn.net/z69183787/article/details/52943917 select&#xff0c;poll&#xff0c;epoll都是IO多路复用的机制。所谓I/O多路复用机制&#xff0c;就是说通过一种机制&#xff0c;可以监视多个描述符…

概念 多路复用 到底是个啥?通俗易懂的理解

前言&#xff1a;教育的公平才是最大的公平&#xff0c;本科和专科还是有点差别的。时间长点学方面会充实些。 先给个结论&#xff1a;I/O多路复用技术&#xff08;就是大家经常说的事件循环&#xff09;实际上&#xff0c;事件驱动模型还有另外一个名字&#xff0c;而且更加出…

Redis的IO多路复用原理

什么是阻塞&#xff0c;非阻塞&#xff0c;异步同步&#xff0c;select&#xff0c;poll&#xff0c;epoll&#xff1f;今天我们用一遍文章解开这多年的迷惑。 首先我们想要通过网络接收消息&#xff0c;是这样的一个步骤。 用户空间向内核空间请求网络数据内核空间把网卡数据…

什么是IO多路复用,理解IO多路复用

什么是IO多路复用&#xff1f; IO 多路复用是一种同步IO模型&#xff0c;实现一个线程可以监视多个文件句柄&#xff1b;一旦某个文件句柄就绪&#xff0c;就能够通知应用程序进行相应的读写操作&#xff1b;没有文件句柄就绪就会阻塞应用程序&#xff0c;交出CPU。 多路是指网…

多路复用与多路分用

从现在开始&#xff0c;我们开始传输层的学习&#xff0c;自顶向下第六版中改成了运输层&#xff0c;感觉怪怪的 书中打了邮政服务和代收发信件的兄弟姐妹之间的比方&#xff0c;非常贴切&#xff0c;这是传输层和网络层的作用区别&#xff0c;也就是说&#xff0c;传输层管的是…

多路复用(

apue 多路复用 需求来自用户&#xff0c;用户的需求来自实际的使用场景。在实际运用中&#xff0c;一个系统或者程序需要处理的事件并不是只有一个或一类&#xff0c;而是存在各种各样的事件在一小段事件内一起发生&#xff0c;此时按照没学多线程的逻辑的处理方式就是这样&…

多路复用

讲多路复用先我觉得有必要讲一下什么是阻塞IO、非阻塞IO、同步IO、异步IO这几个东西&#xff1b;linux的五种IO模型&#xff1a; 1)阻塞I/O&#xff08;blocking I/O&#xff09; 2)非阻塞I/O&#xff08;nonblocking I/O&#xff09; 3) I/O复用(select和poll)&#xff08;…

io多路复用的原理和实现_IO多路复用机制详解

select&#xff0c;poll&#xff0c;epoll机制区别总结: 服务器端编程经常需要构造高性能的IO模型&#xff0c;常见的IO模型有四种&#xff1a; (1)同步阻塞IO(Blocking IO)&#xff1a;即传统的IO模型。 (2)同步非阻塞IO(Non-blocking IO)&#xff1a;默认创建的socket都是…

【多路复用器介绍】

【多路复用器介绍】意义 作用 实现 意义逻辑电路原理结构与真值表逻辑电路 实现代码参考资料 意义 多路复用器将接收的复合数据流&#xff0c;依照信道分离数据&#xff0c;并将它们送到对应的输出线上&#xff0c;故称为解多路复用器。 实际生活中&#xff0c;使用多路复用器…

多路复用技术(频分多路复用、时分多路复用和波分多路复用)

基带信号就是将数字信号1或0直接用两种不同的电压来表示&#xff0c;然后送到线路上去传输。 宽带信号则是将基带信号进行调制后形成的频分复用模拟信号。 多路复用技术的基本原理是&#xff1a;各路信号在进入同一个有线的或无线的传输媒质之前&#xff0c;先采用调制技术把…

8、多路复用技术

这一节&#xff0c;我们介绍信道的多路复用&#xff0c;作为数据通信基础的收尾知识点&#xff0c;这个知识点并没有特别复杂的地方&#xff0c;主要是理解不同的复用技术的特点&#xff0c;在一些考试中也没有多少考点&#xff0c;或者说不做重点。 多路复用技术 先从字面上来…

TCP/IP多路复用

所有网络通信的本质目标就是进程间通信。 除了寻址&#xff08;Addressing&#xff09;&#xff0c;IP 协议还有一个非常重要的能力就是路由。 寻址告诉我们去往下一个目的地该朝哪个方向走&#xff0c;路由则是根据下一个目的地选择路径。寻址更像在导航&#xff0c;路由更像…

多路复用,讲的很明白

作者&#xff1a;罗志宇 链接&#xff1a;https://www.zhihu.com/question/32163005/answer/55772739 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 假设你是一个机场的空管&#xff0c; 你需要管理到你机场的所有…

全网最详细的 I/O 多路复用解析

前言 IO多路复用目前在大厂的面试中&#xff0c;一般在两个地方可能会被问到&#xff0c;一个是在问到网络这一块的时候&#xff0c;另一个是在问到 Redis 这一块的时候&#xff0c;因为 Redis 底层也是使用了IO多路复用&#xff0c;所以整体来说 IO多路复用&#xff0c;也算是…