[开源] .Net ORM FreeSql 1.8.0-preview 最新动态播报
FreeSql 是 .NET 開(kāi)源生態(tài)下的 ORM 輪子,在一些人眼里屬于重復(fù)造輪子:不看也罷。就像昨天有位朋友截圖某培訓(xùn)直播發(fā)給我看,內(nèi)容為:“FreeSQL(個(gè)人產(chǎn)品),自己玩可以,不要商用。ORM框架:1.安全、穩(wěn)定(更新穩(wěn)定、有BUG有人修復(fù),有人升級(jí))”。
這突出其來(lái)的“關(guān)愛(ài)”,讓我的內(nèi)心毫無(wú)波瀾,確實(shí)是毫無(wú)波瀾,比起當(dāng)初 FreeSql 初出茅廬之時(shí)的諷刺友好得多。寫(xiě)在開(kāi)頭的這些內(nèi)容并不祈求這部分人改變觀念,該黑的請(qǐng)繼續(xù)黑,黑總比沒(méi)有關(guān)注好,是吧?我無(wú)所謂你,但是別人呢?麻煩你們不要無(wú)腦抨擊,你們這種行為不知道挽殺了多少社區(qū)項(xiàng)目。
2018 年 12 月份開(kāi)發(fā) FreeSql 到現(xiàn)在,1859 顆星,412 Issues,18 PR,170K 包下載量。說(shuō)明還是有開(kāi)發(fā)者關(guān)注和喜愛(ài),只要有人關(guān)注,就不會(huì)停更不修 BUG 一說(shuō)。大家有興趣可以看看更新記錄,看看我們的代碼提交量,4700+ 單元測(cè)試不說(shuō)非常多,我個(gè)人覺(jué)得已經(jīng)超過(guò)很多國(guó)產(chǎn)項(xiàng)目,有興趣的再去隔壁“國(guó)產(chǎn)第一” ORM 上看看,對(duì)比對(duì)比!如果不更新了,請(qǐng)把位置讓出來(lái);如果有BUG修復(fù)不了,請(qǐng)讓 FreeSql 來(lái);如果不好用,就不要搞一堆 SEO 害人入坑;如果。。。如果。。。
這不是挑釁,看到對(duì)方的 issues 實(shí)在不忍,看到對(duì)方的源碼,哇哦,單元測(cè)試在哪里?好了不廢話(huà)了。。
20個(gè)月了,FreeSql 還活著,而且生命力頑強(qiáng)見(jiàn)下圖:
預(yù)告:年底發(fā)布 2.0.0 版本將凍結(jié)新功能開(kāi)發(fā),不再制造新 BUG,一心修復(fù)老功能引出的 BUG,完善文檔。
本文將介紹在過(guò)去的三個(gè)月完成的一些有意義的功能介紹。
2|0入戲準(zhǔn)備
FreeSql 是 .Net ORM,能支持 .NetFramework4.0+、.NetCore、Xamarin、XAUI、Blazor、以及還有說(shuō)不出來(lái)的運(yùn)行平臺(tái),因?yàn)榇a綠色無(wú)依賴(lài),支持新平臺(tái)非常簡(jiǎn)單。目前單元測(cè)試數(shù)量:4700+,Nuget下載數(shù)量:170K+,源碼幾乎每天都有提交。值得高興的是 FreeSql 加入了 ncc 開(kāi)源社區(qū):https://github.com/dotnetcore/FreeSql,加入組織之后社區(qū)責(zé)任感更大,需要更努力做好品質(zhì),為開(kāi)源社區(qū)出一份力。
QQ群:4336577(已滿(mǎn))、8578575(在線(xiàn))
為什么要重復(fù)造輪子?
FreeSql 主要優(yōu)勢(shì)在于易用性上,基本是開(kāi)箱即用,在不同數(shù)據(jù)庫(kù)之間切換兼容性比較好。作者花了大量的時(shí)間精力在這個(gè)項(xiàng)目,肯請(qǐng)您花半小時(shí)了解下項(xiàng)目,謝謝。
FreeSql 整體的功能特性如下:
支持 CodeFirst 對(duì)比結(jié)構(gòu)變化遷移;
支持 DbFirst 從數(shù)據(jù)庫(kù)導(dǎo)入實(shí)體類(lèi);
支持 豐富的表達(dá)式函數(shù),自定義解析;
支持 批量添加、批量更新、BulkCopy;
支持 導(dǎo)航屬性,貪婪加載、延時(shí)加載、級(jí)聯(lián)保存;
支持 讀寫(xiě)分離、分表分庫(kù),租戶(hù)設(shè)計(jì);
支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/達(dá)夢(mèng)/神通/人大金倉(cāng)/MsAccess;
1.5.0 -> 1.8.0-preview 更新的重要功能如下:
一、增加 $"{a.Code}_{a.Id}" lambda 解析;
二、增加 lambda 表達(dá)式樹(shù)解析子查詢(xún) ToList + string.Join() 產(chǎn)生 類(lèi)似 group_concat 的效果;
三、增加 SqlExt 常用開(kāi)窗函數(shù)的自定義表達(dá)式解析;
四、完善 WhereDynamicFilter 動(dòng)態(tài)過(guò)濾查詢(xún);
五、增加 BeginEdit/EndEdit 批量編輯數(shù)據(jù)的功能;
六、增加 人大金倉(cāng)/神通 數(shù)據(jù)庫(kù)的訪(fǎng)問(wèn)支持;
七、增加 父子表(樹(shù)表)遞歸查詢(xún)、刪除功能;
FreeSql 使用非常簡(jiǎn)單,只需要定義一個(gè) IFreeSql 對(duì)象即可:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, connectionString).UseAutoSyncStructure(true) //自動(dòng)同步實(shí)體結(jié)構(gòu)到數(shù)據(jù)庫(kù).Build(); //請(qǐng)務(wù)必定義成 Singleton 單例模式3|0增加 $"{a.Code}_{a.Id}" lambda 解析;
在之前查詢(xún)數(shù)據(jù)的時(shí)候,$"" 這種語(yǔ)法糖神器居然不能使用在 lambda 表達(dá)式中,實(shí)屬遺憾。現(xiàn)在終于可以了,如下:
var item = fsql.GetRepository<Topic>().Insert(new Topic { Clicks = 101, Title = "我是中國(guó)人101", CreateTime = DateTime.Parse("2020-7-5") }); var sql = fsql.Select<Topic>().WhereDynamic(item).ToSql(a => new {str = $"x{a.Id + 1}z-{a.CreateTime.ToString("yyyyMM")}{a.Title}{a.Title}" }); Assert.Equal($@"SELECT concat('x',ifnull((a.`Id` + 1), ''),'z-',ifnull(date_format(a.`CreateTime`,'%Y%m'), ''),'',ifnull(a.`Title`, ''),'',ifnull(a.`Title`, ''),'') as1 FROM `tb_topic` a WHERE (a.`Id` = {item.Id})", sql);再次說(shuō)明:都是親兒子,并且都有對(duì)應(yīng)的單元測(cè)試,兄臺(tái)大可放心用在不同的數(shù)據(jù)庫(kù)中
4|0增加 lambda 表達(dá)式樹(shù)解析子查詢(xún) ToList + string.Join() 產(chǎn)生 類(lèi)似 group_concat 的效果;
v1.8.0+ string.Join + ToList 實(shí)現(xiàn)將子查詢(xún)的多行結(jié)果,拼接為一個(gè)字符串,如:"1,2,3,4"
fsql.Select<Topic>().ToList(a => new {id = a.Id,concat = string.Join(",", fsql.Select<StringJoin01>().ToList(b => b.Id)) }); //SELECT a.`Id`, (SELECT group_concat(b.`Id` separator ',') // FROM `StringJoin01` b) //FROM `Topic` a該語(yǔ)法,在不同數(shù)據(jù)庫(kù)都作了相應(yīng)的 SQL 翻譯。
5|0增加 SqlExt 常用的自定義表達(dá)式樹(shù)解析;
SqlExt.cs 定義了一些常用的表達(dá)式樹(shù)解析,如下:
fsql.Select<T1, T2>().InnerJoin((a, b) => b.Id == a.Id).ToList((a, b) => new{Id = a.Id,EdiId = b.Id,over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(),case1 = SqlExt.Case().When(a.Id == 1, 10).When(a.Id == 2, 11).When(a.Id == 3, 12).When(a.Id == 4, 13).When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End()).End(), //這里因?yàn)閺?fù)雜才這樣,一般使用三元表達(dá)式即可:a.Id == 1 ? 10 : 11groupct1 = SqlExt.GroupConcat(a.Id).Distinct().OrderBy(b.EdiId).Separator("_").ToValue()});本功能利用 FreeSql 自定義解析實(shí)現(xiàn)常用表達(dá)式樹(shù)解析,歡迎 PR 補(bǔ)充
6|0完善 WhereDynamicFilter 動(dòng)態(tài)過(guò)濾查詢(xún)
是否見(jiàn)過(guò)這樣的高級(jí)查詢(xún)功能,WhereDynamicFilter 在后端可以輕松完成這件事情,前端根據(jù) UI 組裝好對(duì)應(yīng)的 json 字符串傳給后端就行,如下:
DynamicFilterInfo dyfilter = JsonConvert.DeserializeObject<DynamicFilterInfo>(@" {""Logic"" : ""Or"",""Filters"" :[{""Field"" : ""Code"",""Operator"" : ""NotContains"",""Value"" : ""val1"",""Filters"" :[{""Field"" : ""Name"",""Operator"" : ""NotStartsWith"",""Value"" : ""val2"",}]},{""Field"" : ""Parent.Code"",""Operator"" : ""Equals"",""Value"" : ""val11"",""Filters"" :[{""Field"" : ""Parent.Name"",""Operator"" : ""Contains"",""Value"" : ""val22"",}]}] } "); fsql.Select<VM_District_Parent>().WhereDynamicFilter(dyfilter).ToList(); //SELECT a.""Code"", a.""Name"", a.""ParentCode"", a__Parent.""Code"" as4, a__Parent.""Name"" as5, a__Parent.""ParentCode"" as6 //FROM ""D_District"" a //LEFT JOIN ""D_District"" a__Parent ON a__Parent.""Code"" = a.""ParentCode"" //WHERE (not((a.""Code"") LIKE '%val1%') AND not((a.""Name"") LIKE 'val2%') OR a__Parent.""Code"" = 'val11' AND (a__Parent.""Name"") LIKE '%val22%')ISelect.WhereDynamicFilter 方法實(shí)現(xiàn)動(dòng)態(tài)過(guò)濾條件(與前端交互),支持的操作符:
Contains/StartsWith/EndsWith/NotContains/NotStartsWith/NotEndsWith:包含/不包含,like '%xx%',或者 like 'xx%',或者 like '%xx'
Equal/NotEqual:等于/不等于
GreaterThan/GreaterThanOrEqual:大于/大于等于
LessThan/LessThanOrEqual:小于/小于等于
Range:范圍查詢(xún)
DateRange:日期范圍,有特殊處理 value[1] + 1
Any/NotAny:是否符合 value 中任何一項(xiàng)(直白的說(shuō)是 SQL IN)
7|0增加 BeginEdit/EndEdit 批量編輯數(shù)據(jù)的功能;
場(chǎng)景:winform 加載表數(shù)據(jù)后,一頓添加、修改、刪除操作之后,點(diǎn)擊【保存】
[Fact] public void BeginEdit() {fsql.Delete<BeginEdit01>().Where("1=1").ExecuteAffrows();var repo = fsql.GetRepository<BeginEdit01>();var cts = new[] {new BeginEdit01 { Name = "分類(lèi)1" },new BeginEdit01 { Name = "分類(lèi)1_1" },new BeginEdit01 { Name = "分類(lèi)1_2" },new BeginEdit01 { Name = "分類(lèi)1_3" },new BeginEdit01 { Name = "分類(lèi)2" },new BeginEdit01 { Name = "分類(lèi)2_1" },new BeginEdit01 { Name = "分類(lèi)2_2" }}.ToList();repo.Insert(cts);repo.BeginEdit(cts); //開(kāi)始對(duì) cts 進(jìn)行編輯cts.Add(new BeginEdit01 { Name = "分類(lèi)2_3" });cts[0].Name = "123123";cts.RemoveAt(1);Assert.Equal(3, repo.EndEdit()); } class BeginEdit01 {public Guid Id { get; set; }public string Name { get; set; } }上面的代碼 EndEdit 方法執(zhí)行的時(shí)候產(chǎn)生 3 條 SQL 如下:
INSERT INTO "BeginEdit01"("Id", "Name") VALUES('5f26bf07-6ac3-cbe8-00da-7dd74818c3a6', '分類(lèi)2_3')UPDATE "BeginEdit01" SET "Name" = '123123' WHERE ("Id" = '5f26bf00-6ac3-cbe8-00da-7dd01be76e26')DELETE FROM "BeginEdit01" WHERE ("Id" = '5f26bf00-6ac3-cbe8-00da-7dd11bcf54dc')提醒:該操作只對(duì)變量 cts 有效,不是針對(duì)全表對(duì)比更新。
8|0增加 人大金倉(cāng)/神通 數(shù)據(jù)庫(kù)的訪(fǎng)問(wèn)支持
天津神舟通用數(shù)據(jù)技術(shù)有限公司(簡(jiǎn)稱(chēng)“神舟通用公司”),隸屬于中國(guó)航天科技集團(tuán)(CASC)。是國(guó)內(nèi)從事數(shù)據(jù)庫(kù)、大數(shù)據(jù)解決方案和數(shù)據(jù)挖掘分析產(chǎn)品研發(fā)的專(zhuān)業(yè)公司。公司獲得了國(guó)家核高基科技重大專(zhuān)項(xiàng)重點(diǎn)支持,是核高基專(zhuān)項(xiàng)的牽頭承擔(dān)單位。自1993年在航天科技集團(tuán)開(kāi)展數(shù)據(jù)庫(kù)研發(fā)以來(lái),神通數(shù)據(jù)庫(kù)已歷經(jīng)27年的發(fā)展歷程。公司核心產(chǎn)品主要包括神通關(guān)系型數(shù)據(jù)庫(kù)、神通KStore海量數(shù)據(jù)管理系統(tǒng)、神通商業(yè)智能套件等系列產(chǎn)品研發(fā)和市場(chǎng)銷(xiāo)售。基于產(chǎn)品組合,可形成支持交易處理、MPP數(shù)據(jù)庫(kù)集群、數(shù)據(jù)分析與處理等解決方案,可滿(mǎn)足多種應(yīng)用場(chǎng)景需求。產(chǎn)品通過(guò)了國(guó)家保密局涉密信息系統(tǒng)、公安部等保四級(jí)、軍B +級(jí)等安全評(píng)測(cè)和認(rèn)證。
北京人大金倉(cāng)信息技術(shù)股份有限公司(以下簡(jiǎn)稱(chēng)“人大金倉(cāng)”)是具有自主知識(shí)產(chǎn)權(quán)的國(guó)產(chǎn)數(shù)據(jù)管理軟件與服務(wù)提供商。人大金倉(cāng)由中國(guó)人民大學(xué)一批最早在國(guó)內(nèi)開(kāi)展數(shù)據(jù)庫(kù)教學(xué)、科研、開(kāi)發(fā)的專(zhuān)家于1999年發(fā)起創(chuàng)立,先后承擔(dān)了國(guó)家“863”、“核高基”等重大專(zhuān)項(xiàng),研發(fā)出了具有國(guó)際先進(jìn)水平的大型通用數(shù)據(jù)庫(kù)產(chǎn)品。2018年,人大金倉(cāng)申報(bào)的“數(shù)據(jù)庫(kù)管理系統(tǒng)核心技術(shù)的創(chuàng)新與金倉(cāng)數(shù)據(jù)庫(kù)產(chǎn)業(yè)化”項(xiàng)目榮獲2018年度國(guó)家科學(xué)技術(shù)進(jìn)步二等獎(jiǎng),產(chǎn)學(xué)研的融合進(jìn)一步助力國(guó)家信息化建設(shè)。
隨著華為、中興事務(wù),國(guó)產(chǎn)數(shù)據(jù)庫(kù)市場(chǎng)相信是未來(lái)是趨勢(shì)走向,縱觀 .net core 整個(gè)圈子對(duì)國(guó)產(chǎn)神舟通用、人大金倉(cāng)數(shù)據(jù)庫(kù)的支持幾乎為 0,今天 FreeSql ORM 可以使用 CodeFirst/DbFirst 兩種模式進(jìn)行開(kāi)發(fā)。
并且聲稱(chēng):FreeSql 對(duì)各數(shù)據(jù)庫(kù)沒(méi)有親兒子一說(shuō),除了 MsAcces 其他全部是親兒子,在功能提供方面一碗水端平。
眾所周知 EFCore for oracle 問(wèn)題多,并且現(xiàn)在才剛剛更新到 3.x,在這樣的背景下,一個(gè)國(guó)產(chǎn)數(shù)據(jù)庫(kù)更不能指望誰(shuí)實(shí)現(xiàn)好用的 EFCore。目前看來(lái)除了 EFCore for sqlserver 我們沒(méi)把握完全占優(yōu)勢(shì),起碼在其他數(shù)據(jù)庫(kù)肯定是我們更接地氣。
使用 FreeSql 訪(fǎng)問(wèn)人大金倉(cāng)/神通 數(shù)據(jù)庫(kù),只需要修改代碼如下即可:
static IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.ShenTong, connectionString) //修改 DataType 設(shè)置切換數(shù)據(jù)庫(kù).UseAutoSyncStructure(true) //自動(dòng)同步實(shí)體結(jié)構(gòu)到數(shù)據(jù)庫(kù).Build(); //請(qǐng)務(wù)必定義成 Singleton 單例模式9|0增加 父子表(樹(shù)表)遞歸查詢(xún)、刪除功能;
無(wú)限級(jí)分類(lèi)(父子)是一種比較常用的表設(shè)計(jì),每種設(shè)計(jì)方式突出優(yōu)勢(shì)的同時(shí)也帶來(lái)缺陷,如:
方法1:表設(shè)計(jì)中只有 parent_id 字段,困擾:查詢(xún)麻煩(本文可解決);
方法2:表設(shè)計(jì)中冗余子級(jí)id便于查詢(xún),困擾:添加/更新/刪除的時(shí)候需要重新計(jì)算;
方法3:表設(shè)計(jì)中存儲(chǔ)左右值編碼,困擾:同上;
方法1設(shè)計(jì)最簡(jiǎn)單,我們正是解決它設(shè)計(jì)簡(jiǎn)單,使用復(fù)雜的問(wèn)題。
首先,按照導(dǎo)航屬性的定義,定義好父子屬性:
public class Area {[Column(IsPrimary = true)]public string Code { get; set; }public string Name { get; set; }public virtual string ParentCode { get; set; }[Navigate(nameof(ParentCode))]public Area Parent { get; set; }[Navigate(nameof(ParentCode))]public List<Area> Childs { get; set; } }定義 Parent 屬性,在表達(dá)式中可以這樣:
fsql.Select<Area>().Where(a => a.Parent.Parent.Parent.Name == "中國(guó)").First();定義 Childs 屬性,在表達(dá)式中可以這樣(子查詢(xún)):
fsql.Select<Area>().Where(a => a.Childs.AsSelect().Any(c => c.Name == "北京")).First();定義 Childs 屬性,還可以使用【級(jí)聯(lián)保存】、【貪婪加載】?等等操作。
利用級(jí)聯(lián)保存,添加測(cè)試數(shù)據(jù)如下:
fsql.Delete<Area>().Where("1=1").ExecuteAffrows(); var repo = fsql.GetRepository<Area>(); repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; repo.DbContextOptions.NoneParameter = true; repo.Insert(new Area {Code = "100000",Name = "中國(guó)",Childs = new List<Area>(new[] {new Area{Code = "110000",Name = "北京",Childs = new List<Area>(new[] {new Area{ Code="110100", Name = "北京市" },new Area{ Code="110101", Name = "東城區(qū)" },})}}) });功能1:ToTreeList
配置好父子屬性之后,就可以這樣用了:
var t1 = fsql.Select<Area>().ToTreeList(); Assert.Single(t1); Assert.Equal("100000", t1[0].Code); Assert.Single(t1[0].Childs); Assert.Equal("110000", t1[0].Childs[0].Code); Assert.Equal(2, t1[0].Childs[0].Childs.Count); Assert.Equal("110100", t1[0].Childs[0].Childs[0].Code); Assert.Equal("110101", t1[0].Childs[0].Childs[1].Code);查詢(xún)數(shù)據(jù)本來(lái)是平面的,ToTreeList 方法將返回的平面數(shù)據(jù)在內(nèi)存中加工為樹(shù)型 List 返回。
功能2:AsTreeCte 遞歸刪除
很常見(jiàn)的無(wú)限級(jí)分類(lèi)表功能,刪除樹(shù)節(jié)點(diǎn)時(shí),把子節(jié)點(diǎn)也處理一下。
fsql.Select<Area>().Where(a => a.Name == "中國(guó)").AsTreeCte().ToDelete().ExecuteAffrows(); //刪除 中國(guó) 下的所有記錄如果軟刪除:
fsql.Select<Area>().Where(a => a.Name == "中國(guó)").AsTreeCte().ToUpdate().Set(a => a.IsDeleted, true).ExecuteAffrows(); //軟刪除 中國(guó) 下的所有記錄功能3:AsTreeCte 遞歸查詢(xún)
若不做數(shù)據(jù)冗余的無(wú)限級(jí)分類(lèi)表設(shè)計(jì),遞歸查詢(xún)少不了,AsTreeCte 正是解決遞歸查詢(xún)的封裝,方法參數(shù)說(shuō)明:
| (可選) pathSelector | 路徑內(nèi)容選擇,可以設(shè)置查詢(xún)返回:中國(guó) -> 北京 -> 東城區(qū) |
| (可選) up | false(默認(rèn)):由父級(jí)向子級(jí)的遞歸查詢(xún),true:由子級(jí)向父級(jí)的遞歸查詢(xún) |
| (可選) pathSeparator | 設(shè)置 pathSelector 的連接符,默認(rèn):-> |
| (可選) level | 設(shè)置遞歸層級(jí) |
通過(guò)測(cè)試的數(shù)據(jù)庫(kù):MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、達(dá)夢(mèng)、人大金倉(cāng)
姿勢(shì)一:AsTreeCte() + ToTreeList
var t2 = fsql.Select<Area>().Where(a => a.Name == "中國(guó)").AsTreeCte() //查詢(xún) 中國(guó) 下的所有記錄.OrderBy(a => a.Code).ToTreeList(); //非必須,也可以使用 ToList(見(jiàn)姿勢(shì)二) Assert.Single(t2); Assert.Equal("100000", t2[0].Code); Assert.Single(t2[0].Childs); Assert.Equal("110000", t2[0].Childs[0].Code); Assert.Equal(2, t2[0].Childs[0].Childs.Count); Assert.Equal("110100", t2[0].Childs[0].Childs[0].Code); Assert.Equal("110101", t2[0].Childs[0].Childs[1].Code); // WITH "as_tree_cte" // as // ( // SELECT 0 as cte_level, a."Code", a."Name", a."ParentCode" // FROM "Area" a // WHERE (a."Name" = '中國(guó)')// union all// SELECT wct1.cte_level + 1 as cte_level, wct2."Code", wct2."Name", wct2."ParentCode" // FROM "as_tree_cte" wct1 // INNER JOIN "Area" wct2 ON wct2."ParentCode" = wct1."Code" // ) // SELECT a."Code", a."Name", a."ParentCode" // FROM "as_tree_cte" a // ORDER BY a."Code"姿勢(shì)二:AsTreeCte() + ToList
var t3 = fsql.Select<Area>().Where(a => a.Name == "中國(guó)").AsTreeCte().OrderBy(a => a.Code).ToList(); Assert.Equal(4, t3.Count); Assert.Equal("100000", t3[0].Code); Assert.Equal("110000", t3[1].Code); Assert.Equal("110100", t3[2].Code); Assert.Equal("110101", t3[3].Code); //執(zhí)行的 SQL 與姿勢(shì)一相同姿勢(shì)三:AsTreeCte(pathSelector) + ToList
設(shè)置 pathSelector 參數(shù)后,如何返回隱藏字段?
var t4 = fsql.Select<Area>().Where(a => a.Name == "中國(guó)").AsTreeCte(a => a.Name + "[" + a.Code + "]").OrderBy(a => a.Code).ToList(a => new {item = a,level = Convert.ToInt32("a.cte_level"),path = "a.cte_path" }); Assert.Equal(4, t4.Count); Assert.Equal("100000", t4[0].item.Code); Assert.Equal("110000", t4[1].item.Code); Assert.Equal("110100", t4[2].item.Code); Assert.Equal("110101", t4[3].item.Code); Assert.Equal("中國(guó)[100000]", t4[0].path); Assert.Equal("中國(guó)[100000] -> 北京[110000]", t4[1].path); Assert.Equal("中國(guó)[100000] -> 北京[110000] -> 北京市[110100]", t4[2].path); Assert.Equal("中國(guó)[100000] -> 北京[110000] -> 東城區(qū)[110101]", t4[3].path); // WITH "as_tree_cte" // as // ( // SELECT 0 as cte_level, a."Name" || '[' || a."Code" || ']' as cte_path, a."Code", a."Name", a."ParentCode" // FROM "Area" a // WHERE (a."Name" = '中國(guó)')// union all// SELECT wct1.cte_level + 1 as cte_level, wct1.cte_path || ' -> ' || wct2."Name" || '[' || wct2."Code" || ']' as cte_path, wct2."Code", wct2."Name", wct2."ParentCode" // FROM "as_tree_cte" wct1 // INNER JOIN "Area" wct2 ON wct2."ParentCode" = wct1."Code" // ) // SELECT a."Code" as1, a."Name" as2, a."ParentCode" as5, a.cte_level as6, a.cte_path as7 // FROM "as_tree_cte" a // ORDER BY a."Code"更多姿勢(shì)...請(qǐng)根據(jù)代碼注釋進(jìn)行嘗試
10|0寫(xiě)在最后
作者的努力,喜歡能打動(dòng)到你,希望正在使用的、善良的您能動(dòng)一動(dòng)小手指,把文章轉(zhuǎn)發(fā)一下,讓更多人知道 .NET 有這樣一個(gè)好用的 ORM 存在。謝謝了!!
FreeSql 開(kāi)源協(xié)議 MIT https://github.com/dotnetcore/FreeSql,可以商用,文檔齊全。QQ群:4336577(已滿(mǎn))、8578575(在線(xiàn))
如果你有好的 ORM 實(shí)現(xiàn)想法,歡迎給作者留言討論,謝謝觀看!
總結(jié)
以上是生活随笔為你收集整理的[开源] .Net ORM FreeSql 1.8.0-preview 最新动态播报的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 初识ABP vNext(1):开篇计划基
- 下一篇: 懂程序员的产品经理是什么样子?