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

ORM一直是长久不衰的话题,各类重复造轮子的过程一直在进行,轮子都同样是圆的,你的又有什么特色呢?html

CRL这个轮子造了好多年,功能也愈来愈标准完备,在开发过程当中,解决了不少问题,先上一张脑图描述CRL的功能git

开发框架的意义在于github

  • 开发更标准,更统一,不会由于不一样人写的代码不同
  • 开发效率更高,无需从新造轮子,重复无用的代码,同时简化开发流程
  • 运行效率获得控制,程序稳定性获得提升

围绕这几点,抛开常规的增删改查,咱们来说些不同凡响的sql

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

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

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

var context = new MsSqlContext(ConnectionString);

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

然而根据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还提供了手动维护方法,使可以按实体结构重建/检查数据表
也提供了对象结构文档导出,不用提心文档的问题
详细介绍看这里
http://www.javashuo.com/article/p-ztrwtyds-s.html

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();

 

基于这样的形式,能够将全部查询都走缓存,不再用担忧数据库查询效率了,简值中小项目开发利器
详细介绍看这里
http://www.javashuo.com/article/p-twhvddck-be.html

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,统统不是问题
详细介绍看这里
http://www.javashuo.com/article/p-wrchqswn-ms.html

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服务器

一张图代表一切(不一样机器实际状况可能有差别)

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

相关文章
相关标签/搜索