欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ORM之炀,打造自已独特的开发框架CRL

程序员文章站 2022-06-22 10:57:49
ORM一直是长久不衰的话题,各种重复造*的过程一直在进行,*都一样是圆的,你的又有什么特点呢? CRL这个*造了好多年,功能也越来越标准完备,在开发过程中,解决了很多问题,先上一张脑图描述CRL的功能 开发框架的意义在于 开发更标准,更统一,不会因为不同人写的代码不一样 开发效率更高,无需重新 ......

orm一直是长久不衰的话题,各种重复造*的过程一直在进行,*都一样是圆的,你的又有什么特点呢?

crl这个*造了好多年,功能也越来越标准完备,在开发过程中,解决了很多问题,先上一张脑图描述crl的功能

ORM之炀,打造自已独特的开发框架CRL

开发框架的意义在于

  • 开发更标准,更统一,不会因为不同人写的代码不一样
  • 开发效率更高,无需重新造*,重复无用的代码,同时简化开发流程
  • 运行效率得到控制,程序稳定性得到提高

围绕这几点,抛开常规的增删改查,我们来讲些与众不同的

1.与众不同之一,动态数据源,天生适合分库分表

可动态配置的功能总比静态的灵活,扩展性强

目前看到的框架多数访问对象实例化都类似于

var context = new mssqlcontext(connectionstring);

在对象初始时,就绑定上了数据库连接串, 这样写没什么问题,但是不好扩展
如:需要动态切换库,表,根据租户信息访问不同的数据库,或不同类型的数据库,或是读写分离,这时,急需处理的技术问题就来了,分库分表的解决方案,读写分离的方案
在数据连接绑定的情况下,这种问题很不好解决
又或者传入多个连接串,在调用时,手动选择调用的库或表,对于这种方式,只能说耦合太严重,得关心配置,又得关心调用,在crl之前的版本里,有这样实现过,弃用了

然而根据ioc的理念,这种问题也不是不好解决,让数据访问对象抽象化实现就能办到了
数据查询方法不再直接调用数据访问对象,而是调用抽象工厂方法,由抽象工厂方法来实例化访问对象,过程表示为

数据查询方法(组件内) => 抽象工厂(组件内) => 抽象实现(组件外)

基于这样的理念,crl在设计之初,就使用了的这样的方式,以代码为例

!数据访问实现

以下实现了分库分表和mongodb切换


以下在程序启动时初始

var builder = new crl.settingconfigbuilder();
            builder.usemongodb();//引用crl.mongo 使用mongodb
                                 //注册自定义定位,按membersharding传入数据定义数据源位置
                                 //注册一
            builder.registerlocation<code.sharding.membersharding>((t, a) =>
            {
                var tablename = t.tablename;
                if (a.name == "hubro")//当名称为hubro,则定位到库testdb2 表membersharding1
                {
                    tablename = "membersharding1";
                    return new crl.sharding.location("testdb2", tablename);
                }
                //返回定位库和表名
                return new crl.sharding.location("testdb", tablename);
            });
            //注册二
            builder.registerdbaccessbuild(dblocation =>
            {
                if (dblocation.managename == "mongo")
                {
                    var conn = crl.core.customsetting.getconfigkey("mongodb");
                    return new crl.dbaccessbuild(dbtype.mongodb, conn);
                }
                return null;
            });
            //注册三
            builder.registerdbaccessbuild(dblocation =>
            {
                //自定义定位,由注册一传入
                if (dblocation.shardinglocation != null)
                {
                    return new crl.dbaccessbuild(dbtype.mssql, "data source=.;initial catalog=" + dblocation.shardinglocation.databasename + ";user id=sa;password=123");
                }
                return new crl.dbaccessbuild(dbtype.mssql, "server=.;database=testdb; uid=sa;pwd=123;");
            });

 

!数据访问类,类似于仓储的形式,根据实际业务实现
定位使用示例

public class membermanage : crl.sharding.baseprovider<membersharding>
{
}
var instance=new membermanage();
instance.add(new membersharding(){name="hubro"});

根据定位规则 运行到注册一,此数据将会插入到 库testdb2 表membersharding1

常规切换示例

public class mongodbtestmanage : crl.baseprovider<mongodbmodel2>
{
    public override string managename => "mongo";
}
var instance=new mongodbtestmanage();
instance.add(new mongodbmodel2(){name="hubro"});

根据数据访问规则,运行到注册二,此数据将会插入mongodb

可以看到,在上面代码中,没有看到任何数据连接串的传入,数据的访问都由初始时动态分配,对于方法调用是不透明的,调用者不用关心数据源的问题

2.与众不同之二,表结构自动维护

在新技术的支持下,程序和数据库的绑定关系越来越模糊,现在可能是用的sqlserver,回头可能改成mysql了,或者改成mongodb
依赖数据库开发变成越来越不可取,效率也很低
再后来出现了dbfirst方式,虽解决了部份问题,但也很麻烦,如:

建立数据库模型=>导入数据库=>t4模版生成代码(修修补补)

而使用crl后,过程一步到位,在别人还在用pm设计表结构索引时,你已经设计好了业务结构,效率杠杠的

编写实体类,实现对象访问=>调试运行,自动创建表结构(关键字,长度,索引)

同时,crl还提供了手动维护方法,使能够按实体结构重建/检查数据表
也提供了对象结构文档导出,不用提心文档的问题
详细介绍看这里

3.与众不同之三,动态缓存

使用缓存可以大大提高程序的运行效率,使用redis或mongodb之类的又需要额外维护
对于单应用程序,程序集内缓存非常有用
crl内置了缓存实现和维护
只需按方法调用就行了,缓存创建维护全自动
如:
从数据库查

var item = instance.queryitem(b => b.id==1)

从缓存查

var item = instance.queryitemfromcache(b=>b.id==1);

也支持按查询自定义缓存

var query = code.productdatamanage.instance.getlambdaquery();
//缓存会按条件不同缓存不同的数据,条件不固定时,慎用
query.where(b => b.id < 700);
int exp = 10;//过期分钟
query.expire(exp);
var list = query.tolist();

 

基于这样的形式,可以将所有查询都走缓存,再也不用担心数据库查询效率了,简值中小项目开发利器
详细介绍看这里

4.与众不同之四,应对复杂查询

因为没有查询分支的概念,处理复杂的查询,一票orm估计得退场了,虽然合理的结构设计会减少查询复杂度,但谁能保证呢
crl查询分支过程如下

主查询 => createquery子查询 => 返回匿名对象筛选lambdaqueryresultselect => 主查询嵌套子查询 => 返回结果

理论上只要符合调用逻辑,可以无限嵌套
示例:

var q1 = code.ordermanage.instance.getlambdaquery();//主查询
            var q2 = q1.createquery<code.productdata>();//创建一个子查询
            q2.where(b => b.id > 0);
            var view = q2.createquery<code.member>().groupby(b => b.name).where(b => b.id > 0).select(b => new { b.name, aa = b.id.count() });//group查询
            var view2 = q2.join(view, (a, b) => a.categoryname == b.name).select((a, b) => new { ss1 = a.userid, ss2 = b.aa });//关联group
            q1.join(view2, (a, b) => a.id == b.ss1).select((a, b) => new { a.id, b.ss1 });//再关联
            var result = view2.tolist();
            var sql = q1.tostring();

生成sql打印如下

select t1.[id] as id,
t2.[ss1] as ss1
from [orderproduct] t1 with(nolock)
inner join
(select t2.[userid] as ss1,
t3.[aa] as ss2
from [productdata] t2 with(nolock)
inner join
(select t3.[name] as name,
count(t3.id) as aa
from [member] t3 with(nolock)
where (t3.[id]>@par1)
group by t3.[name]) t3 on (t2.[categoryname]=t3.[name])
where (t2.[id]>@par0) ) t2 on (t1.[id]=t2.[ss1])

 

不管是join后再group,还是group后再group,还是group后再join,通通不是问题
详细介绍看这里

5.与众不同之五,查询抽象,非关系型数据库支持

通过对lambda表达式的解析,可以实现不同的查询转换,如mongodb,或elasticsearch(目前只实现了mongodb)
有人问,这样有什么用呢?
好处就是,在crl框架下,一套lambdaquery走天下,不用写各种差异很大的查询方法了,在动态数据源的支持下,数据拆分游刃有余
如:
之前有个报表存在mssql里,发现数据量太大了,查询慢,改由mongodb,程序不用怎么调整,直接在配置里改为mongodb即可

以mongodb为例

crllambdaquery=>crlexpression=>bsondocument=>mongodb

在[数据访问实现]示例中,演示了如何切换到mongodb
代码实现见项目:crl.mongo

6.题外之六,请使用仓储模式

在上文提到,好多框架会直接返回一个数据访问对象,如

var obj1context.query<testentity>(b=>b.id==1).tosingle();

然而这样会导致滥用,直接在web层用,在service层随意用,如

var obj2=context.query<testentity2>(b=>b.id==1).tosingle();
var obj3=context.query<testentity3>(b=>b.id==1).tosingle();

某一天,testentity3要换库了,查找一下引用,傻眼了,上百个引用(接手别人的项目,亲身体验过这种痛苦,一个个改)
好在crl开始就杜绝了这种情况发生,对据的访问必须通过baseprovider实现,而baseprovider就是一个仓储的形式

7.题外之七,查询效率

orm效率无非分两点,实体映射效率和语法解析效率,

对于映射反映在,一次返回多行数据,转换为实体集合

对于语法解析效率,按参数调用多次,返回一行数据,转换为实体

测式程序和sql为本机,cpu空闲正常,2核6g服务器

一张图表明一切(不同机器实际情况可能有差异)

ORM之炀,打造自已独特的开发框架CRL

crl效率虽不是最高的,但也不是最差的,测试项目见:

https://github.com/hubro-xx/crl5/tree/master/test/crltest

 

大概列举了以上几项,还有好多特有的东西,*好不好,东西南北滚滚试试

crl开发框架虽然写好长时间,但一直在debug状态中, 最近又升级了,分离了数据访问层,不同数据库引用不同的数据访问层,数据访问层实现也很简单,只需要写两个文件,如mysql,实现mysqlhelper和mysqldbadapter
见:https://github.com/hubro-xx/crl5/tree/master/crl.providers/crl.mysql
同时,版本也升级到5.1,项目结构发生了改变

源码地址:https://github.com/hubro-xx/crl5

crl目前.net版本为.net 4.5, 有时间了再整理整理netstandard版本

除了orm,crl还带 动态api,rpc,websocket,api客户端代理实现
https://www.cnblogs.com/hubro/p/11652687.html
微服务注册,发现,调用集成参见:
https://github.com/hubro-xx/crl5/blob/master/consul/consultest/program.cs