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;
十年磨一劍,在代碼寫得愈來愈深刻,再回頭看自已的代碼,殘破不堪.
歡迎下載源碼交流討論