ORM一直是長久不衰的話題,各種重復(fù)造輪子的過程一直在進(jìn)行,輪子都一樣是圓的,你的又有什么特點(diǎn)呢? CRL這個(gè)輪子造了好多年,功能也越來越標(biāo)準(zhǔn)完備,在開發(fā)過程中,解決了很多問題,先上一張腦圖描述CRL的功能 開發(fā)框架的意義在于
圍繞這幾點(diǎn),拋開常規(guī)的增刪改查,我們來講些與眾不同的 1.與眾不同之一,動(dòng)態(tài)數(shù)據(jù)源,天生適合分庫分表可動(dòng)態(tài)配置的功能總比靜態(tài)的靈活,擴(kuò)展性強(qiáng) 目前看到的框架多數(shù)訪問對象實(shí)例化都類似于 var context = new MsSqlContext(ConnectionString); 在對象初始時(shí),就綁定上了數(shù)據(jù)庫連接串, 這樣寫沒什么問題,但是不好擴(kuò)展 然而根據(jù)IOC的理念,這種問題也不是不好解決,讓數(shù)據(jù)訪問對象抽象化實(shí)現(xiàn)就能辦到了 數(shù)據(jù)查詢方法(組件內(nèi)) => 抽象工廠(組件內(nèi)) => 抽象實(shí)現(xiàn)(組件外) 基于這樣的理念,CRL在設(shè)計(jì)之初,就使用了的這樣的方式,以代碼為例 !數(shù)據(jù)訪問實(shí)現(xiàn) 以下實(shí)現(xiàn)了分庫分表和mongoDB切換
var builder = new CRL.SettingConfigBuilder(); builder.UseMongoDB();//引用CRL.Mongo 使用MongoDB //注冊自定義定位,按MemberSharding傳入數(shù)據(jù)定義數(shù)據(jù)源位置 //注冊一builder.RegisterLocation<Code.Sharding.MemberSharding>((t, a) =>{var tableName = t.TableName;if (a.Name == "hubro")//當(dāng)名稱為hubro,則定位到庫testdb2 表MemberSharding1 { tableName = "MemberSharding1";return new CRL.Sharding.Location("testdb2", tableName); }//返回定位庫和表名return new CRL.Sharding.Location("testdb", tableName); });//注冊二builder.RegisterDBAccessBuild(dbLocation =>{if (dbLocation.ManageName == "mongo") {var conn = CRL.Core.CustomSetting.GetConfigKey("mongodb");return new CRL.DBAccessBuild(DBType.MongoDB, conn); }return null; });//注冊三builder.RegisterDBAccessBuild(dbLocation =>{//自定義定位,由注冊一傳入if (dbLocation.ShardingLocation != null) {return new CRL.DBAccessBuild(DBType.MSSQL, "Data Source=.;Initial Catalog=" + dbLocation.ShardingLocation.DataBaseName + ";User ID=sa;Password=123"); }return new CRL.DBAccessBuild(DBType.MSSQL, "server=.;database=testDb; uid=sa;pwd=123;"); }); !數(shù)據(jù)訪問類,類似于倉儲(chǔ)的形式,根據(jù)實(shí)際業(yè)務(wù)實(shí)現(xiàn) public class MemberManage : CRL.Sharding.BaseProvider<MemberSharding>{ }var instance=new MemberManage(); instance.Add(new MemberSharding(){Name="hubro"}); 根據(jù)定位規(guī)則 運(yùn)行到注冊一,此數(shù)據(jù)將會(huì)插入到 庫testdb2 表MemberSharding1 常規(guī)切換示例 public class MongoDBTestManage : CRL.BaseProvider<MongoDBModel2> { public override string ManageName => "mongo"; } var instance=new MongoDBTestManage(); instance.Add(new MongoDBModel2(){name="hubro"}); 根據(jù)數(shù)據(jù)訪問規(guī)則,運(yùn)行到注冊二,此數(shù)據(jù)將會(huì)插入mongodb 可以看到,在上面代碼中,沒有看到任何數(shù)據(jù)連接串的傳入,數(shù)據(jù)的訪問都由初始時(shí)動(dòng)態(tài)分配,對于方法調(diào)用是不透明的,調(diào)用者不用關(guān)心數(shù)據(jù)源的問題 2.與眾不同之二,表結(jié)構(gòu)自動(dòng)維護(hù)在新技術(shù)的支持下,程序和數(shù)據(jù)庫的綁定關(guān)系越來越模糊,現(xiàn)在可能是用的SQLSERVER,回頭可能改成MySql了,或者改成mongoDB 建立數(shù)據(jù)庫模型=>導(dǎo)入數(shù)據(jù)庫=>T4模版生成代碼(修修補(bǔ)補(bǔ)) 而使用CRL后,過程一步到位,在別人還在用PM設(shè)計(jì)表結(jié)構(gòu)索引時(shí),你已經(jīng)設(shè)計(jì)好了業(yè)務(wù)結(jié)構(gòu),效率杠杠的 編寫實(shí)體類,實(shí)現(xiàn)對象訪問=>調(diào)試運(yùn)行,自動(dòng)創(chuàng)建表結(jié)構(gòu)(關(guān)鍵字,長度,索引) 同時(shí),CRL還提供了手動(dòng)維護(hù)方法,使能夠按實(shí)體結(jié)構(gòu)重建/檢查數(shù)據(jù)表 3.與眾不同之三,動(dòng)態(tài)緩存使用緩存可以大大提高程序的運(yùn)行效率,使用REDIS或MONGODB之類的又需要額外維護(hù) var item = instance.QueryItem(b => b.Id==1) 從緩存查 var item = instance.QueryItemFromCache(b=>b.Id==1); 也支持按查詢自定義緩存 var query = Code.ProductDataManage.Instance.GetLambdaQuery();//緩存會(huì)按條件不同緩存不同的數(shù)據(jù),條件不固定時(shí),慎用query.Where(b => b.Id < 700);int exp = 10;//過期分鐘query.Expire(exp);var list = query.ToList(); 基于這樣的形式,可以將所有查詢都走緩存,再也不用擔(dān)心數(shù)據(jù)庫查詢效率了,簡值中小項(xiàng)目開發(fā)利器 4.與眾不同之四,應(yīng)對復(fù)雜查詢因?yàn)闆]有查詢分支的概念,處理復(fù)雜的查詢,一票O(jiān)RM估計(jì)得退場了,雖然合理的結(jié)構(gòu)設(shè)計(jì)會(huì)減少查詢復(fù)雜度,但誰能保證呢 主查詢 => CreateQuery子查詢 => 返回匿名對象篩選LambdaQueryResultSelect => 主查詢嵌套子查詢 => 返回結(jié)果 理論上只要符合調(diào)用邏輯,可以無限嵌套 var q1 = Code.OrderManage.Instance.GetLambdaQuery();//主查詢var q2 = q1.CreateQuery<Code.ProductData>();//創(chuàng)建一個(gè)子查詢q2.Where(b => b.Id > 0);var view = q2.CreateQuery<Code.Member>().GroupBy(b => b.Name).Where(b => b.Id > 0).Select(b => new { b.Name, aa = b.Id.COUNT() });//GROUP查詢var view2 = q2.Join(view, (a, b) => a.CategoryName == b.Name).Select((a, b) => new { ss1 = a.UserId, ss2 = b.aa });//關(guān)聯(lián)GROUPq1.Join(view2, (a, b) => a.Id == b.ss1).Select((a, b) => new { a.Id, b.ss1 });//再關(guān)聯(lián)var result = view2.ToList();var sql = q1.ToString(); 生成SQL打印如下 SELECT t1.[Id] AS Id, t2.[ss1] AS ss1FROM [OrderProduct] t1 with(nolock)INNER JOIN(SELECT t2.[UserId] AS ss1, t3.[aa] AS ss2FROM [ProductData] t2 with(nolock)INNER JOIN(SELECT t3.[Name] AS Name,COUNT(t3.Id) AS aaFROM [Member] t3 with(nolock)WHERE (t3.[Id]>@par1)GROUP BY t3.[Name]) t3 ON (t2.[CategoryName]=t3.[Name])WHERE (t2.[Id]>@par0) ) t2 ON (t1.[Id]=t2.[ss1]) 不管是JOIN后再GROUP,還是GROUP后再GROUP,還是GROUP后再JOIN,通通不是問題 5.與眾不同之五,查詢抽象,非關(guān)系型數(shù)據(jù)庫支持通過對Lambda表達(dá)式的解析,可以實(shí)現(xiàn)不同的查詢轉(zhuǎn)換,如MongoDB,或ElasticSearch(目前只實(shí)現(xiàn)了MongoDB) 以MongoDB為例 CRLLambdaQuery=>CRLExpression=>BsonDocument=>MongoDB 在[數(shù)據(jù)訪問實(shí)現(xiàn)]示例中,演示了如何切換到MongoDB 6.題外之六,請使用倉儲(chǔ)模式在上文提到,好多框架會(huì)直接返回一個(gè)數(shù)據(jù)訪問對象,如 var obj1context.Query<TestEntity>(b=>b.Id==1).ToSingle(); 然而這樣會(huì)導(dǎo)致濫用,直接在WEB層用,在Service層隨意用,如 var obj2=context.Query<TestEntity2>(b=>b.Id==1).ToSingle();var obj3=context.Query<TestEntity3>(b=>b.Id==1).ToSingle(); 某一天,TestEntity3要換庫了,查找一下引用,傻眼了,上百個(gè)引用(接手別人的項(xiàng)目,親身體驗(yàn)過這種痛苦,一個(gè)個(gè)改) 7.題外之七,查詢效率ORM效率無非分兩點(diǎn),實(shí)體映射效率和語法解析效率, 對于映射反映在,一次返回多行數(shù)據(jù),轉(zhuǎn)換為實(shí)體集合 對于語法解析效率,按參數(shù)調(diào)用多次,返回一行數(shù)據(jù),轉(zhuǎn)換為實(shí)體 測式程序和SQL為本機(jī),CPU空閑正常,2核6G服務(wù)器 一張圖表明一切(不同機(jī)器實(shí)際情況可能有差異) CRL效率雖不是最高的,但也不是最差的,測試項(xiàng)目見: https://github.com/hubro-xx/CRL5/tree/master/Test/TestConsole 大概列舉了以上幾項(xiàng),還有好多特有的東西,輪子好不好,東西南北滾滾試試 CRL開發(fā)框架雖然寫好長時(shí)間,但一直在DEBUG狀態(tài)中, 最近又升級(jí)了,分離了數(shù)據(jù)訪問層,不同數(shù)據(jù)庫引用不同的數(shù)據(jù)訪問層,數(shù)據(jù)訪問層實(shí)現(xiàn)也很簡單,只需要寫兩個(gè)文件,如MySql,實(shí)現(xiàn)MySqlHelper和MySQLDBAdapter 源碼地址:https://github.com/hubro-xx/CRL5 CRL目前.NET版本為.net 4.5, 有時(shí)間了再整理整理netstandard版本 除了ORM,CRL還帶 動(dòng)態(tài)API,RPC,WebSocket,api客戶端代理實(shí)現(xiàn) |
|