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快速开发框 ...
随机推荐
- jstree 获取选中节点的所有子子点
//加载功能树 function initTree() { $.jstree.destroy(); $.ajax({ type: "Get", url: "/Depart ...
- 设计模式的征途—11.外观(Facade)模式
在软件开发中,有时候为了完成一项较为复杂的功能,一个类需要和多个其他业务类交互,而这些需要交互的业务类经常会作为一个完整的整体出现,由于涉及的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服 ...
- 使用一个for循环将N*N的二维数组的所有值置1
<?php // 使用一个for循环将N*N的二维数组的所有值置1 $n = ; $a = []; ;$i<$n*$n;$i++){ $a[$i/$n][$i%$n] = ; } prin ...
- vim操作命令
一,命令模式下 文件顶部: gg 文件底部: G 删除当前行:dd 删除当前行,并进入INSERT模式: cc 取消删除:u
- 《Metasploit魔鬼训练营》第三章
p85 使用nmap探测目标主机的操作系统版本那里有问题,我探测不了NAT服务器的! msf > nmap -sT 10.10.10.254 [*] exec: nmap -sT 10.10.1 ...
- Linux笔记(固定USB摄像头硬件端口,绑定前后置摄像头)
在Android的系统会有前置摄像头和后置摄像头的定义,摄像头分为SOC类型的摄像头和USB这一类的摄像头,接下要分析就是USB摄像头这一类 . 一般在android或者linux系统中分析一个模块, ...
- "MySql.Data.MySqIClient.MySqlProviderSevices”违反了继承安全 性规则。派生类型必须与基类型的安全可访问性匹配或者比基类型的安 全可访问性低。 "解决方法
写Code First 时(使用的是MySql数据库),添加好EntityFrame.MySql.Data .MySql.Data.Entity后 ,写好TestDbContext类. 运行时报出一个 ...
- A:点排序-poj
A:点排序 总时间限制: 1000ms 内存限制: 65536kB 描述 给定一个点的坐标(x, y),在输入的n个点中,依次计算这些点到指定点的距离,并按照距离进行从小到大排序,并且输出点的坐标 ...
- ptrdiff_t 和 size_t
size_t和ptrdiff_t常常用来指示数组长度. size_t常用于表示数组的大小,可以一般的将他看为 typedef unsigned int size_t,实质是一个无符号整形.包含在头文件 ...
- c语言贪吃蛇详解3.让蛇动起来
c语言贪吃蛇详解3.让蛇动起来 前几天的实验室培训课后作业我布置了贪吃蛇,今天有时间就来写一下题解.我将分几步来教大家写一个贪吃蛇小游戏.由于大家c语言未学完,这个教程只涉及数组和函数等知识点. 上次 ...