CRL快速开发框架升级到4.52,谈谈开发过程中的优化
CRL4.5版本已经稳定使用于目前的几个中型项目中,在实际使用中,也发现了不少问题,这些问题都在4.52中提交
CRL具体功能和使用请浏览 CRL快速开发框架系列教程
由于现在项目是一套业务系统,查询需求比较多,CRL带的语法解析都能满足,特殊的可手写SQL,在针对查询的优化,以下几点
对查询调用的监视
由于业务封装写得非常复杂,方法嵌套很严重,无法检查一个方法内有多少查询,需不需要优化,因此使用CallContext进行了监视,并生成报表
public ActionResult RunTime()
{
var str = CRL.Runtime.RunTimeService.Display();
return Content(str);
}
最终如下图:

路径:表示调用方法的路径
DBCall:表示实例化的数据管理类
ALLCall:表示所有数据访问调用
表达式解析内存占用
在一次更新中,为了使解析速度更快,将表达式进行了缓存,只有参数进行了重解析,看上去是省了不少,如下所示
CRLExpression.CRLExpression BinaryExpressionHandler(Expression left, Expression right, ExpressionType expType)
var key = string.Format("{0}{1}{2}{3}", __PrefixsAllKey, left, expType, right);
var a = BinaryExpressionCache.TryGetValue(key, out cacheItem);
if (a)
{
返回缓存
}
但是Expression left.ToString()效率并不高,并且占内存,所以这个并没有什么卵用
字符串变量内存占用
因为CRL查询所有参数都需要进行参数化,因此需要对应的参数名,如以下表达式:b=>b.Id==1
参数名为:@p1,若再有参数,依次类推,然而@p1由 string.Format("{0}p{1}","@",1)生成,在对性能测试时发现,这个Format占用了很多内存
于是解决办法,还是缓存参数名,提前生成,重复使用
if (parameDic == null)
{
parameDic = new Dictionary<int, string>();
for (int i = 0; i <= 5000; i++)
{
parameDic.Add(i, __DBAdapter.GetParamName("p", i));
}
}
var _par = parameDic[parIndex];
AddParame(_par, par);
字段格式化内存占用
CRL查询默认是查询所有字段,所以查询为 select t1.Id,t1.Name,t1.Code.....,在没有手动选择查询字段时,这些select其实一直是一样的,通过性能监视发现,好多内存被这重复解析占用了
优化后,当是查询所有字段,从缓存里生成
if (GetPrefix(__MainType) == "t1.")
{
key = __MainType.ToString();
SelectFieldInfo value;
var a = queryFieldCache.TryGetValue(key, out value);
if (a)
{
if (!cacheAllFieldString)
{
var item = value.Clone();
item.CleanQueryFieldString();
_CurrentSelectFieldCache = item;
}
else
{
_CurrentSelectFieldCache = value;
}
return;
}
cache = true;
}
因为CRL对查询作了关键字处理,包括表名,字段名,所以最终的语句为
以MSSQL为例:select t1.[Name] from [table1] t1
方法为
public override string KeyWordFormat(string value)
{
return string.Format("[{0}]", value);
}
实际上,每个字段只用生成一次就行了,再次使用时从缓存中取,节省了不少内占用
public string FieldNameFormat(Attribute.FieldAttribute field)
{
if (string.IsNullOrEmpty(field.MapingNameFormat))
{
field.MapingNameFormat = KeyWordFormat(field.MapingName);
}
return field.MapingNameFormat;
}
手写语句提取参数
直接将参数拼在SQL里好像不怎么雅观,并且,容易造成语法错误和注入漏洞,如下:
string sql = "select top 10 Id,ProductId,ProductName1 from ProductData a where a.addtime>='2017-09-01' and a.id=234";
var helper = DBExtend;
var list = helper.ExecDynamicList(sql);
CRL新增了方法,能重新处理手写的SQL为参数化
int parIndex = 1;
/// <summary>
/// 提取SQL参数
/// </summary>
/// <param name="db"></param>
/// <param name="sql"></param>
/// <param name="manual"></param>
/// <returns></returns>
public virtual string ReplaceParameter(CoreHelper.DBHelper db,string sql,bool manual = false)
{
if (!SettingConfig.ReplaceSqlParameter && !manual)
{
return sql;
}
//return sql;
var re = @"((\s|,)*)(\w+)\s*(>|<|=|!=|>=|<=)\s*('(.*?)'|([1-9]\d*.\d*|0.\d*[1-9]\d*))(\s|,|\))";
sql = sql + " ";
if (!Regex.IsMatch(sql, re, RegexOptions.IgnoreCase))
{
return sql;
}
Regex r = new Regex(re, RegexOptions.IgnoreCase);
List<string> pars = new List<string>();
//int index = 1;
for (var m = r.Match(sql); m.Success; m = m.NextMatch())
{
var name = m.Groups[3];
var op = m.Groups[4];
var value1 = m.Groups[6];
var value2 = m.Groups[7];
var value = string.IsNullOrEmpty(value2.Value) ? value1 : value2;
var p = m.Groups[1];
var p2 = m.Groups[8];
var pName = GetParamName("_p", parIndex);
db.AddParam(pName, value.ToString());
sql = sql.Replace(m.ToString(), string.Format("{0}{1}{4}{2}{3} ", p, name, pName, p2, op));
parIndex += 1;
}
return sql;
}
在配置为自动替换SQL拼接参数(CRL.SettingConfig.ReplaceSqlParameter=true),实际输出将为:
select top 10 Id,ProductId,ProductName1 from ProductData a where a.addtime>=@p1 and a.id=@p2
异步插入MSMQ的实现
在某个业务中,一张表很频繁的单个插入,占用大量资源,专门为这写个消息队列好像不怎么高明,下次又有这样的情况怎么办
于是有就了,为每个对象定义自已的消息队列和处理,用第三方的不太好集成,就是微软自家的吧
/// <summary>
/// 添加一条记录[基本方法]
/// 异步时,会定时执行批量插入,依赖MSMQ服务
/// </summary>
/// <param name="p"></param>
/// <param name="asyn">异步插入</param>
public virtual void Add(TModel p, bool asyn = false)
调用参数为true时,则为TModel类型创建消息队列,并异步分批次插入到数据库中,比如在10秒内连续调用了100次此方法
只是将数据存入了消息队列,在队列下个处理周期,将这100条批量插入到数据库,效率倍增
DbSet方式的实现
在Entity Framework里,有DbSet的概念,配置好数据关系后,关联对象直接就能取到了
在CRL里,简单实现一下
public class Order : CRL.IModelBase
{
public CRL.Set.DbSet<ProductData> Products//返回关联的Product
{
get
{
return GetDbSet<ProductData>(b => b.Id, ProductId);
}
}
public CRL.Set.EntityRelation<Member> Member//返回关联的Member
{
get
{
return GetEntityRelation<Member>(b => b.Id, UserId);
}
}
}
调用如下:
var order = new Code.Order();
//所有
var product = order.Products.ToList();
//返回关联过的查询,使用完整查询满足更多需求
var product2 = order.Products.GetQuery();
var p = new Code.ProductData() { BarCode = "33333" };
//添加一项
order.Products.Add(p);
order.Products.Delete(p);//删除一项
//返回完整的BaseProvider
var provider = order.Products.GetProvider();
//返回关联的member,在调用时返回,在循环内调用会多次调用数据库
var member = order.Member.Value;
十年磨一剑,在代码写得越来越深入,再回头看自已的代码,残破不堪.
欢迎下载源码交流讨论
获取CRL最新源码见文章底部下载
CRL快速开发框架升级到4.52,谈谈开发过程中的优化的更多相关文章
- 非关系型数据库来了,CRL快速开发框架升级到版本4
轮子?,我很任性,我要造不一样的轮子,同时支持关系型和非关系型的框架有没有 新版数据查询作了些调整,抽象了LabmdaQueryy和DBExtend,升级到版本4,非关系数据库MongoDB被支持了! ...
- CRL快速开发框架升级到3.1
CRL是一款面向对象的轻量级ORM框架,本着快速开发,使用简便的原则,设计为 无需关心数据库结构,CRL自动维护创建,即写即用(CRL内部有表结构检查机制,保证表结构一致性) 无需第三方工具生成代理类 ...
- CRL快速开发框架开源完全转到Github
CRL简介 CRL是一款面向对象的轻量级ORM框架,本着快速开发,使用简便的原则,设计为 无需关心数据库结构,CRL自动维护创建,即写即用(CRL内部有表结构检查机制,保证表结构一致性) 无需第三方工 ...
- CRL快速开发框架系列教程十三(嵌套查询)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- CRL快速开发框架系列教程十二(MongoDB支持)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- CRL快速开发框架系列教程十一(大数据分库分表解决方案)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- CRL快速开发框架系列教程十(导出对象结构)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- CRL快速开发框架系列教程九(导入/导出数据)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- CRL快速开发框架系列教程七(使用事务)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
随机推荐
- c++/cmake /Android NDK 动态链接库交叉编译笔记
项目使用cmake管理,由于项目的需要,核心代码要求跨 Linux/Windows/Android 三平台.Windows和Linux都好说,但Android NDK费了一番功夫还是没有解决.临时的解 ...
- 使用Recovery Services备份文件及文件夹
1.创建恢复服务保管库 2.在backup项中选择本地,备份文件和文件夹选项 3.根据提示下载Agent及保管库凭据 4.在文件服务器上安装Agent 5.选择"继续注册"项,并添 ...
- mybatis逆向工程之生成文件解释
一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 int countByExample(UserExample example) thorws SQLException ...
- webstorm激活破解码+++使用技巧
Webstorm激活破解码 2017-06-15更新 之前都是使用2017.2.27的方法,版本是2017.1.1,还没提示过期,但是根据评论说这个链接已经失效了,评论也给出了个新地址:http:// ...
- sql 触发器,看完后对CHK有更深的理解
触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. 什么是触发器? 触发器对表进行插入.更新.删除 ...
- Python 学习之路3
接下来把剩下的实验一起写上去 实验2 写一个学生类,属性有学号,姓名,成绩(三门),方法有输出,求平均成绩. 设计思路: 1. 先写一个学生类,并向里面写一个求平均值和输出信息的方法. ...
- gulp基础操作实践
按照gulp中文文档对gulp基础操作的一些实践练习,记录以防忘掉. 一,选择并输出文件:gulp.src(globs[,options]) eg:gulp.src('src/less/index.l ...
- laravel查看执行sql的
加个监听就好了~~~~而且很简单 1.在routes.php(api.php\web.php)中添加如下语句 Event::listen('illuminate.query', function($s ...
- pku夏令营面试
北大面试题目: 一.内存交换 内存交换(对换)的基本思想是,把处于等待状态(或在CPU调度原则下被剥夺运行权利) 的程序从内存移到辅存,把内存空间腾出来,这一过程又叫换出:把准备好竞争CPU运行的程序 ...
- 非一屏页面,出现遮罩层页面位置不动,并且遮罩层一屏显示。(pc,移动端都适用的方法)
首先展示页面效果: 遮罩没出现的页面张酱紫:页面在楼层二这个位置. 遮罩显示:后面页面页面任停留在当前浏览位置,滚动条并未回顶部 下面来说说写法: css: 页面具体布局样式......(此处省略无数 ...