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,谈谈开发过程中的优化的更多相关文章

  1. 非关系型数据库来了,CRL快速开发框架升级到版本4

    轮子?,我很任性,我要造不一样的轮子,同时支持关系型和非关系型的框架有没有 新版数据查询作了些调整,抽象了LabmdaQueryy和DBExtend,升级到版本4,非关系数据库MongoDB被支持了! ...

  2. CRL快速开发框架升级到3.1

    CRL是一款面向对象的轻量级ORM框架,本着快速开发,使用简便的原则,设计为 无需关心数据库结构,CRL自动维护创建,即写即用(CRL内部有表结构检查机制,保证表结构一致性) 无需第三方工具生成代理类 ...

  3. CRL快速开发框架开源完全转到Github

    CRL简介 CRL是一款面向对象的轻量级ORM框架,本着快速开发,使用简便的原则,设计为 无需关心数据库结构,CRL自动维护创建,即写即用(CRL内部有表结构检查机制,保证表结构一致性) 无需第三方工 ...

  4. CRL快速开发框架系列教程十三(嵌套查询)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  5. CRL快速开发框架系列教程十二(MongoDB支持)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. CRL快速开发框架系列教程十一(大数据分库分表解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  7. CRL快速开发框架系列教程十(导出对象结构)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  8. CRL快速开发框架系列教程九(导入/导出数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  9. CRL快速开发框架系列教程七(使用事务)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

随机推荐

  1. [转载] Java集合框架之小结

    转载自http://jiangzhengjun.iteye.com/blog/553191 1.Java容器类库的简化图,下面是集合类库更加完备的图.包括抽象类和遗留构件(不包括Queue的实现): ...

  2. selenium3.7+ python3 添加cookie模拟登陆

    一.背景介绍 最近做一个爬虫项目,用selenium调用浏览器去获取渲染后的源码,但是每次登陆都需要手机验证,这真的是头痛啊,这种验证方式不要想着去破解,还是老老实实用手机收验证码去吧!反正我是不知道 ...

  3. 第二个MapReduce

    大家在学习Hadoop的MapReduce的时候,90%的第一个程序都是WordCount,所以在这里分享一下我的第二个MapReduce程序.对于学习编程语言的人来说,有时候代码是最好的沟通方式之一 ...

  4. [最短路]P1119 灾后重建

    题目背景 B地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响.但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车.换句话说,只有连接着两个重建完成的村庄的公路才能 ...

  5. 流式处理的新贵 Kafka Stream - Kafka设计解析(七)

    原创文章,转载请务必将下面这段话置于文章开头处. 本文转发自技术世界,原文链接 http://www.jasongj.com/kafka/kafka_stream/ Kafka Stream背景 Ka ...

  6. ASP.NET中HttpContext.Cache的使用

    -------------------------------键 --值-----依赖-----过期时间-------------------------------绝对过期------------- ...

  7. MapReduce 入门之一步步自实现词频统计功能

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7687120.html ------------------------------------ ...

  8. 十六、Hadoop学习笔记————Zookeeper实战

    所有服务器都会先将自己的服务器信息注册到servers中,然后每台服务器都会尝试注册master,哪台注册成功,则哪台就是master服务器. 所有的服务器都会关注master节点的删除事件,这样通过 ...

  9. 2386:Lake Counting-poj

    总时间限制:  1000ms 内存限制:  65536kB 描述 Due to recent rains, water has pooled in various places in Farmer J ...

  10. Gulp livereload

    平时使用yeoman作为前端部署工具,感觉到yeoman构建工具虽然方便,但是速度和大小总是不尽人意. 最近看到了gulp http://gulpjs.com/ 比较感兴趣随动手一试 gulp的安装以 ...