CRL快速開發框架升級到4.52,談談開發過程當中的優化

CRL4.5版本已經穩定使用於目前的幾個中型項目中,在實際使用中,也發現了很多問題,這些問題都在4.52中提交html

CRL具體功能和使用請瀏覽 CRL快速開發框架系列教程sql

因爲如今項目是一套業務系統,查詢需求比較多,CRL帶的語法解析都能知足,特殊的可手寫SQL,在針對查詢的優化,如下幾點數據庫

 

對查詢調用的監視api

因爲業務封裝寫得很是複雜,方法嵌套很嚴重,沒法檢查一個方法內有多少查詢,需不須要優化,所以使用CallContext進行了監視,並生成報表緩存

        public ActionResult RunTime()
        {
            var str = CRL.Runtime.RunTimeService.Display();
            return Content(str);
        }

最終以下圖:框架

路徑:表示調用方法的路徑異步

DBCall:表示實例化的數據管理類ide

ALLCall:表示全部數據訪問調用post

 

表達式解析內存佔用性能

在一次更新中,爲了使解析速度更快,將表達式進行了緩存,只有參數進行了重解析,看上去是省了很多,以下所示

 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最新源碼見文章底部下載

相關文章
相關標籤/搜索