前 言
各種懶惰,各種拖沓,終究是要動筆寫終結篇了,在這個系列的前幾篇文章里我們主要學習linq的基礎語法以及他對內存數據的操作等,那么本篇文章我們將討論學習最為大家所熟悉的,也是最受爭議的 Linq To SQL,再次強調,如果你到目前為止認為LinqToSql就是linq的話,有以下幾種方式可共君選擇:1.把這個系列的前面幾篇文章給讀了。2.到菜市場賣塊豆腐給撞了。3.(MM可以忽略跳過哈)把屁股洗干凈,讓大家把你菊花給爆了。
用 意
Linq To Sql 相對現在來說,不可否認它已經過時了,伴隨着vs2010和Entity Fromwork 4的出現,linq to sql 退出歷史舞台是必然的,因為EF4比之更強大更完善。但是linq to sql 並不是一無是處,有很多東西它與EF4是相通的,簡單的了解linq to sql並無害處,並且還可以對EF4有一定的過渡幫助。
由於這個主題能講的內容非常多,篇幅關系不能全部說完,在這里只能簡單地向大家分享個大概,敬請諒解。
目 錄
什么是Linq To Sql
摘自MSDN:LINQ to SQL 是 .NET Framework 3.5 版的一個組件,提供了用於將關系數據作為對象管理的運行時基礎結構。 在 LINQ to SQL 中,關系數據庫的數據模型映射到用開發人員所用的編程語言表示的對象模型。 當應用程序運行時,LINQ to SQL 會將對象模型中的語言集成查詢轉換為 SQL,然后將它們發送到數據庫進行執行。當數據庫返回結果時,LINQ to SQL 會將它們轉換回您可以用您自己的編程語言處理的對象。簡單的理解就是我們對數據進行實體化操作,例如我們可以吧每章表作為一個數據實體封裝操作。
生成實體
在linq to sql中,實體對象時一個非常重要的環節,他是對數據表,視圖等對象的映射,沒有實體就談不上linq to sql了。也許有些老手會反對為什么是生成實體而不是手寫實體,生成實體會產生冗余代碼。個人認為對於初學者來說,我們很多手頭上的項目通常總是先有庫表后有代碼,那么我們會針對庫表進行編寫實體,這真的是個體力活沒有任何捷徑可言,一張一張表的寫實體非常痛苦。所以干脆讓大家直接生成,即省事又方便,而且也可以學到怎樣編寫比較專業的實體。
既然是生成實體,那么這肯定需要一些而外的工具了,在這里MS自VS2008起就給我們提供了這么一個工具SqlMetal.exe命令工具,可為 LINQ to SQL 組件生成代碼和映射。那么接下來我們演示如何生成實體
1.假設我們有一張用戶表,如圖:
2.打開VS2008命令行工具,如圖:
3.輸入命令,生成數據實體。注意:生成實體文件分別有2種,一種是*.cs文件和*.xml映射文件的組合方式,另一種則是*.dbml文件,只能二選一,切記!!
首先我們先生成第一種:*.cs和*.xml組合方式,如圖:
4.根據命令指定的位置,我們可以看到對應的生成文件,如圖:
5.將生成的文件放入我們的項目中,如圖:
注意,在這里我們要選中“linqToSqlMap.xml”,在屬性對話框里設置始終復制到輸出目錄里,如圖:
6.接下來,我們看看類文件,生成了那些實體代碼,如圖:
xml 映射配置文件
從上圖看,生成的代碼貌似有點多,但是這要比我們自己手寫代碼更專業。實體文件主要分為兩部分,一是數據庫上下文關聯類 LinqToSqlDemo,二是對應表的實體類 Users 。到這里我們對第一種組合方式的實體生成就已完成。
接下來我們看看要是我們使用的是生成*.dbml文件又會什么樣的場景呢。
1.同上,輸入命令生成文件,如圖:
2.查看生成文件,如圖:
3.將生成的DBML文件放入項目里,我們可以看到,生成的只有一個文件,但是當添加到項目里時,項目會自動生成layout和designer兩個文件,如圖:
4.有意思東西來了,右鍵點擊dbml文件,選擇視圖設計器,我們可以在編輯框中得到實體映射編輯視圖
5.我們看一下這個時候在*.designer.cs文件里生成了哪些內容
可以看到,生成的實體文件和上一種方式生成的實體文件區別不大,由於沒有了XML映射配置,所以這里采用的是特性映射配置,在Users實體類中我們可以看到附加了一些如Table,Column的特性標記。
到此我們對實體生成的做法有了一個基本的認識,接下來我們看看linq 是怎么通過實體進行增刪改查的
LinqToSql 增刪改查
1.DataContext 實例
既然要對數據進行CURD,那么我們就需通過對數據庫上下文關聯類的實例進行操作之,即DataContext派生類,如上面生成的LinqToSqlDemo派生類。
由於DataContext 具有多個重載構造函數,在這里針對先前的2種實體生成方式對具體的2個構造函數進行描述,其他的就不逐個介紹少了。詳情可以查閱MSDN
如果使用的是*.cs 與 *.xml組合方式的實體映射,那么在構造實例應該如下
如果使用的是*.dbml文件的實體映射,那么就簡單了,直接提供數據庫連接字符串就可以,因為在派生類的內部已經指定使用特性映射配置。見下圖
2.查詢數據
// 假設我們已構造了DataContext對象實例dataContext // 屬性log是實例內部的操作日志輸出,它屬於Stream類型
dataContext.Log = Console.Out; var users = from usr in dataContext.Users select usr; foreach (var usr in users) { Console.WriteLine("用戶名:{0},Email:{1}", usr.UserName, usr.Email); } Console.Read();
輸出結果:
如果我們帶上where 條件,查詢的操作又是如何的呢
// 假設我們已構造了DataContext對象實例dataContext // 屬性log是實例內部的操作日志輸出,它屬於Stream類型 dataContext.Log = Console.Out; var users = from usr in dataContext.Users where usr.UserName == "張三" select usr; foreach (var usr in users) { Console.WriteLine("用戶名:{0},Email:{1}", usr.UserName, usr.Email); } Console.Read();
輸出結果:(這里我們可以看到sql使用了參數化查詢)
3.關聯查詢
往往在實際項目中我們會涉及到幾個表的關聯查詢,那么LinqToSql有時怎樣支持的呢。
假設多了一張用戶詳細表,他與用戶表的關系如下:
生成實體(*.dbml):
在生成的實體代碼*.designer.cs文件中我們會看到,Users 實體類多了一個EntityRef<UserDetails> _UserDetails私有字段,而在UserDetails實體類中對了一個EntityRef<Users> _Users私有字段,泛型類EntityRef<T>是關鍵,他是實體之間關聯關系處理主要對象。篇幅關系詳情請查閱MSDN點擊這里
查詢:
// 假設我們已構造了DataContext對象實例dataContext // 屬性log是實例內部的操作日志輸出,它屬於Stream類型 dataContext.Log = Console.Out; var users = from usr in dataContext.Users select usr; foreach (var usr in users) { Console.WriteLine("用戶名:{0},Email:{1},年齡{2},住址:{3}", usr.UserName, usr.Email, usr.UserDetails.Age, usr.UserDetails.Address); } Console.Read();
輸出結果:
從結果我們可以看到,首先是把用戶表給查了,然后根據linq延遲加載的特性,只有真正使用時才執行,因此當需要查看用戶詳細信息時才會去執行用戶詳細查詢,這樣就帶來了很大弊端,如果數據量大時那么這樣的查詢開銷就大了,大大降低了程序的效率。那么這個問題是否可以解決呢?答案是肯定的,請看下面代碼:
// DataLoadOption數據導入操作對象,它可以告訴linq在執行查詢是否延遲 // 查詢對象的子對象 var loadOption = new DataLoadOptions(); // 設置數據導入對象關聯關系 loadOption.LoadWith<Users>(usr => usr.UserDetails); dataContext.LoadOptions = loadOption; var users = from usr in dataContext.Usersselect usr; foreach (var usr in users) { Console.WriteLine("用戶名:{0},Email:{1},年齡:{2},住址:{3}", usr.UserName, usr.Email, usr.UserDetails.Age, usr.UserDetails.Address); } Console.Read();
輸出結果:
從log我們可以看到這樣就是一條語句查出來數據結果集。注意,這里演示的是2表關系的查詢,如果當我們再多出一個表,而這表是與用戶詳細表形成關聯關系的時候那么,DataLoadoption 就沒法解決了一次性查出,而又回到類似上一個樣例分次查出來。例如:
假設新增一張表(UserDetails2):
關系如下:
生成*.dbml:
查詢:
// 假設我們已構造了DataContext對象實例dataContext // 屬性log是實例內部的操作日志輸出,它屬於Stream類型 dataContext.Log = Console.Out; // DataLoadOption數據導入操作對象,它可以告訴linq在執行查詢是否延遲 // 查詢對象的子對象 var loadOption = new DataLoadOptions(); // 設置數據導入對象關聯關系 loadOption.LoadWith<Users>(usr => usr.UserDetails);
// 加入對表UserDetails2的關聯 loadOption.LoadWith<UserDetails>(dtl => dtl.UserDetails2); dataContext.LoadOptions = loadOption; var users = from usr in dataContext.Users select usr; foreach (var usr in users) { Console.Write("用戶名:{0},Email:{1},年齡:{2},住址:{3}", usr.UserName, usr.Email, usr.UserDetails.Age, usr.UserDetails.Address); foreach (var dtl in usr.UserDetails.UserDetails2) { Console.Write(",性別:{0}", dtl.Sex); } Console.Write("\r\n"); } Console.Read();
查詢結果:
這里我們可以看到,在第一個層級使用了一次查詢,但是往下一個層級查詢UserDetails2表時又變回了分開查詢,效率就大打折扣了。
4.數據新增
通過執行Table<TEneity>類的方法 InsertOnSubmit 新增數據
var dataContext = new LinqToSqlDemo(CONN_STR); // 新增用戶 dataContext.Users.InsertOnSubmit( new Users { UserId = 4, UserName = "趙六", Email = "wangw@xxx.com", UserDetails = new UserDetails { Address = "xxx大道", Age = 25, UserId = 4 } }); // 注意,這里是必須的,提交修改 dataContext.SubmitChanges();
5.數據刪除
通過執行Table<TEntity>類的方法 DeleteOnSubmit 進行刪除數據
var dataContext = new LinqToSqlDemo(CONN_STR); var user = (from usr in dataContext.Users where usr.UserName == "張三" select usr).SingleOrDefault(); dataContext.Users.Attach(user); // 上傳數據 dataContext.Users.DeleteOnSubmit(user);
6.數據更新
在對實體更新完畢后直接調用DataContext的SubmitChanges方法即可。
var dataContext = new LinqToSqlDemo(CONN_STR); var user = (from usr in dataContext.Users where usr.UserName == "張三" select usr).SingleOrDefault(); user.UserName = "張三三"; dataContext.Users.Attach(user, false); // 更新數據 dataContext.SubmitChanges();
注意,在數據刪除和更新的時候我們可以看到都執行了Table<TEntity>類的Attach方法,它是允許反序列化的實體與 DataContext 的新實例關聯,以便可以從數據庫更新或刪除這些實體,篇幅關系,詳情可以查閱MSDN。
拓 展
通過上面我們對LinqToSql有了一個最基本的知識,由於篇幅關系,沒法在一篇文章里逐一詳談,下面是一些進階內容,希望對大家有所幫助
Linq To Sql 白皮書(英文版,非常詳細,想在這方面有深入研究的就花一點時間啃吧)
優缺雜談
總的來說linq To sql 還是不錯的,把討厭的sql命令從我們項目里移除出去,從實體出發,更貼近OOP。不過有時候生成的代碼雖然專業,但是冗余的還是有不少,同時對linq To Sql 生成的SQL語句確實不太感冒效率並不是很高,在查詢時還會帶上不需要的字段,因此如果是小項目快速開發還行,大項目還是請各位看官多斟酌斟酌吧。
總 結
到這,關於linq的這個系列算是結束了,通過這個系列大家應該對linq有了一個最基本的認識,因此光光掌握這個系列所結束的知識在實戰應用中是不夠的,需要各位看官在往后的自我學習中去查閱更多的相關知識做更多的實踐,園子里對linq的介紹文章有很多,大家都不妨去閱讀一下。正所謂師傅領進門,修行靠個人,希望大家都學習linq,掌握linq,個個都是linq高手,以便鄙人向大家學習,三人行必有我師焉。最后謝謝您的閱讀,有說得不對之處請指正,謝謝。
索 引
到現在為止你還未觸碰LINQ,那進來吧 —— LINQ入門(上篇)
到現在為止你還未觸碰LINQ,那進來吧 —— LINQ入門(中篇)
到現在為止你還未觸碰LINQ,那進來吧 —— LINQ入門(下篇)