本篇內(nèi)容主要講解“LINQ to SQL如何實現(xiàn)數(shù)據(jù)訪問通用基類”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“LINQ to SQL如何實現(xiàn)數(shù)據(jù)訪問通用基類”吧!
創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),施甸企業(yè)網(wǎng)站建設(shè),施甸品牌網(wǎng)站建設(shè),網(wǎng)站定制,施甸網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,施甸網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。
不幸的是,當(dāng)你對LINQ進(jìn)行仔細(xì)研究后,我發(fā)現(xiàn)在多層架構(gòu)中使用LINQ的并不是十分容易。
本文介紹用LINQ to SQL實現(xiàn)數(shù)據(jù)層的典型的問題點 ,并提供了一個簡單,方便和靈活的方式來克服它們。
本文附帶的LINQ to SQL 實現(xiàn)數(shù)據(jù)訪問通用類有以下的特點:
實現(xiàn)了存儲庫模式,你可以用不到10行代碼執(zhí)行LINQ實體類型的CRUD (Create, Update, Delete)操作。
無縫協(xié)作,支持LINQ斷開模式(Disconnected LINQ Mode)。
在單一數(shù)據(jù)庫和LINQ實體間支持透明的數(shù)據(jù)庫更新和數(shù)據(jù)加載。
提供為一種方便的功能,在調(diào)試你的應(yīng)用程尋時候,它把所有執(zhí)行的SQL語句輸出控制臺。
本文將假定您對LINQ to SQL (也稱為DLINQ )有一個基本的了解并如何使用它。否則,,,回到此網(wǎng)頁,看看本教程入門系列,如何在多層次應(yīng)用中使用LINQ to SQL。
存在的問題
如果您只是在你的UI層直接用LinqToDataSource對象銜接數(shù)據(jù)庫,那LINQ to SQL太容易使用了。但是,這種做法不完全面向?qū)ο螅?dāng)然也不是一個可取的架構(gòu),除非你是為了快速編碼和臟亂的應(yīng)用程序,并且最終沒有去擴(kuò)展的它打算。
相反,大多數(shù)開發(fā)人員把它們的應(yīng)用程序劃分成若干層,如下:
數(shù)據(jù)訪問層(Data Access Layer)
業(yè)務(wù)層 (Business Layer)
用戶界面層(UI Layer)
這就是所謂的多層數(shù)據(jù)庫應(yīng)用程序設(shè)計。LINQ to SQL將用于數(shù)據(jù)訪問層。
LINQ to SQL的問題是-盡管它的許多優(yōu)點-但是如果要實現(xiàn)數(shù)據(jù)層并不是很簡單。
請看下面的數(shù)據(jù)庫模式(database schema):
一旦你要加載和保存LINQ實體到同一個的數(shù)據(jù)上下文實例(data context instance)(這就是所謂“連接模式”),用LINQ實現(xiàn)數(shù)據(jù)層非常直接。
例如,讓我們從數(shù)據(jù)庫中獲取實體編號為1的客戶,改變屬性first name為“Homer”后在重新儲存到數(shù)據(jù)庫中。在一個多層數(shù)據(jù)庫應(yīng)用程序中,在UI或業(yè)務(wù)層的某個地方的代碼可能看起來就像這樣:
view plaincopy to clipboardprint? 1. 2. //create a new repository instance 3. CustomersRepository customersRepository = new CustomersRepository(); 4. //load a customer instance and change it's FirstName; 5. Customer customer = customersRepository.Load(2); 6. customer.FirstName = "Homer"; 7. //commmit customer to database 8. customersRepository.Save(customer); |
最簡單的方法來實現(xiàn)上面使用到的數(shù)據(jù)層加載和保存功能是:
view plaincopy to clipboardprint? 1. 2. static DataClassesDataContext context=new DataClassesDataContext(); 3. public Customer Load(int CustomerID) 4. { 5. return context.Customers.Single(c => c.ID == CustomerID); 6. } 7. public void Save(Customer toSave) 8. { 9. context.SubmitChanges(); 10. } |
這種方法是使用連接LINQ模式:數(shù)據(jù)上下文(data context)在當(dāng)前作用域一直有效(譯者注:一直保持連接狀態(tài)),所以在把實體保存到數(shù)據(jù)庫的時候,它總是可以重復(fù)使用。其中仍然連接到它。
當(dāng)然,這種做法方便并且在上述的單個例子中能運行,但它存在嚴(yán)重的并發(fā)問題,因為一個數(shù)據(jù)庫方面是用于所有數(shù)據(jù)庫操作。
當(dāng)調(diào)用方法Save(),bmitChanges提交的不僅僅是當(dāng)前Save 方法參數(shù)相關(guān)的LINQ實體,還包括所有改變了的實體。
但是及時把這個缺陷考慮在一邊,使用LINQ在一個多層ASP.NET應(yīng)用程序中,您還不能以相同方式實現(xiàn)數(shù)據(jù)層。首先,可能要求是這樣,在一個頁面請求中,LINQ實體被加載,然后在下一個頁面請求中,它更新并儲存到數(shù)據(jù)庫中的。.同時,您的原始數(shù)據(jù)上下文在當(dāng)前作用域內(nèi)已經(jīng)無效的(譯者住:HTTP協(xié)議是無狀態(tài)的),造成的您的LINQ實體游離。
還有許多其他情況下你需要使用斷開LINQ模式:例如您實現(xiàn)的數(shù)據(jù)庫層可能要作為一個Web服務(wù),提交(commit)以前序列化LINQ實體到數(shù)據(jù)庫等等。
用斷開模式的LINQ to SQL實現(xiàn)數(shù)據(jù)訪問層
所以,在斷開的LINQ模式下,我們?nèi)绾螌崿F(xiàn)數(shù)據(jù)層的Save( )方法?
我們必須
Detach the entity from the old data context從舊的數(shù)據(jù)上下文中分離實體
Create a new data context創(chuàng)建一個新的數(shù)據(jù)上下文
Attach the entity to the new context附加實體到新的數(shù)據(jù)上下文
Submit changes提交更改
在源代碼,它看起來像這樣:
view plaincopy to clipboardprint? 1. 2. public Customer Load(int CustomerID) 3. { 4. DataClassesDataContext context = new DataClassesDataContext(); 5. return context.Customers.Single(c => c.ID == CustomerID); 6. } 7. 8. public void Save(Customer toSave) 9. { 10. //the old data context is no more, we need to create a new one 11. DataClassesDataContext context = new DataClassesDataContext(); 12. //serialize and deserialize the entity to detach it from the 13. //old data context. This is not part of .NET, I am calling 14. //my own code here 15. toSave = EntityDetacher.Detach(toSave); 16. //is the entity new or just updated? 17. //ID is the customer table's identity column, so new entities should 18. //have an ID == 0 19. if (toSave.ID == 0) 20. { 21. //insert entity into Customers table 22. context.Customers.InsertOnSubmit(toSave); 23. } 24. else 25. { 26. //attach entity to Customers table and mark it as "changed" 27. context.Customers.Attach(toSave, true); 28. } 29. } |
現(xiàn)在只要你喜歡,您可以加載修改任意多實體,并且只提交他們一部分到數(shù)據(jù)庫。但由于使用斷開的LINQ ,這個程序并不會感知到LINQ實體之間的關(guān)系。
例如,假設(shè)在業(yè)務(wù)層或用戶界面層您要做到以下幾點:
view plaincopy to clipboardprint? 1. 2. //load currently selected customer from database 3. Customer customer = new CustomersRepository().Load(1); 4. //change the customer's first name 5. customer.FirstName = "Homer"; 6. //add a new bill with two billingitems to the customer 7. Bill newbill = new Bill 8. { 9. Date = DateTime.Now, 10. BillingItems = 11. { 12. new BillingItem(){ItemPrice=10, NumItems=2}, 13. new BillingItem(){ItemPrice=15, NumItems=1} 14. } 15. }; 16. customer.Bills.Add(newbill); 17. //create a new provider to simulate new ASP.NET page request 18. //save the customer 19. new CustomersRepository().Save(customer); |
這個斷開模式下,上述Save( )方法將提交變更到FirstName列,但是忽略了new bill和billing items。為了做到這一點,我們還需要附加或插入遞歸所有相關(guān)的子實體(child entities):
view plaincopy to clipboardprint?1. 2. public void Save(Customer toSave) 3. { 4. //the old data context is no more, we need to create a new one 5. DataClassesDataContext context = new DataClassesDataContext(); 6. //serialize and deserialize the entity to detach it from the 7. //old data context. This is not part of .NET, I am calling 8. //my own code here 9. toSave = EntityDetacher.Detach(toSave); 10. //is the entity new or just updated? 11. //ID is the customer table's identity column, so new entities should 12. //have an ID == 0 13. if (toSave.ID == 0) 14. { 15. //insert entity into Customers table 16. context.Customers.InsertOnSubmit(toSave); 17. } 18. else 19. { 20. //attach entity to Customers table and mark it as "changed" 21. context.Customers.Attach(toSave, true); 22. } 23. //attach or save all "bill" child entities 24. foreach (Bill bill in toSave.Bills) 25. { 26. if (bill.ID == 0) 27. { 28. context.Bills.InsertOnSubmit(bill); 29. } 30. else 31. 32. { 33. context.Bills.Attach(bill, true); 34. } 35. //attach or save all "BillingItem" child entities 36. foreach (BillingItem billingitem in bill.BillingItems) 37. { 38. if (bill.ID == 0) 39. { 40. context.BillingItems.InsertOnSubmit(billingitem); 41. } 42. else 43. { 44. context.BillingItems.Attach(billingitem, true); 45. } 46. } 47. } 48. }
不是很復(fù)雜,但很多打字(譯者注:翻譯不是很難,但要一句句的理解,還要打很多字)。并且這只是支持一個微不足道的database scheme和一個單一的實體類型。.想象一下,如果實現(xiàn)數(shù)據(jù)庫層有幾十個實體類型與幾十個外鍵關(guān)系,在這個數(shù)據(jù)存儲類中,你將要為每一個LINQ實體寫幾十套foreach循環(huán),這不僅是單調(diào)乏味,而且還容易出錯。.當(dāng)你添加新的表,你必須添加幾十foreach循環(huán)。
如何避免這些問題
在相當(dāng)多的在線調(diào)研后,我實現(xiàn)了一個RepositoryBase類,使用他您可以快速實現(xiàn)您的數(shù)據(jù)層,所示為測試通過的例子。 首先,用對象關(guān)系映射器(譯者注:Visual Studio自帶工具)來產(chǎn)生序列化的LINQ實體:在Visual Studio中打開dbml文件,在空白區(qū)域某處左鍵單擊,彈出屬性窗口,設(shè)置“Serialization Mode屬性”為“Unidirectional”。
現(xiàn)在您可以繼承RepositoryBase實現(xiàn)您自己的Repository:
view plaincopy to clipboardprint? 1. 2. public void Save(Customer toSave) 3. { 4. //the old data context is no more, we need to create a new one 5. DataClassesDataContext context = new DataClassesDataContext(); 6. //serialize and deserialize the entity to detach it from the 7. //old data context. This is not part of .NET, I am calling 8. //my own code here 9. toSave = EntityDetacher.Detach(toSave); 10. //is the entity new or just updated? 11. //ID is the customer table's identity column, so new entities should 12. //have an ID == 0 13. if (toSave.ID == 0) 14. { 15. //insert entity into Customers table 16. context.Customers.InsertOnSubmit(toSave); 17. } 18. else 19. { 20. //attach entity to Customers table and mark it as "changed" 21. context.Customers.Attach(toSave, true); 22. } 23. //attach or save all "bill" child entities 24. foreach (Bill bill in toSave.Bills) 25. { 26. if (bill.ID == 0) 27. { 28. context.Bills.InsertOnSubmit(bill); 29. } 30. else 31. 32. { 33. context.Bills.Attach(bill, true); 34. } 35. //attach or save all "BillingItem" child entities 36. foreach (BillingItem billingitem in bill.BillingItems) 37. { 38. if (bill.ID == 0) 39. { 40. context.BillingItems.InsertOnSubmit(billingitem); 41. } 42. else 43. { 44. context.BillingItems.Attach(billingitem, true); 45. } 46. } 47. } 48. } |
您的每一個實體的類型都照這樣做,你就擁有了一個工作在斷開模式下無縫數(shù)據(jù)層。您繼承Repository的類自動執(zhí)行下列方法:
作為錦上添花的功能,在應(yīng)用程序調(diào)試的過程中,你還可以通過輸出控制臺看到執(zhí)行對數(shù)據(jù)庫的操作的SQL命令。這多虧了被用于RepositoryBase的SQL調(diào)試輸出的Kris Vandermotten 方便的DebuggerWriter組件(譯者注:外國人就是紳士)!
天下有沒有免費的午餐...
當(dāng)前的加載(Load)操作中,沒有任何顯著的性能損失,但是當(dāng)你調(diào)用Save or Delete方法時候,幕后用到一點反射(reflection)操作。
對于絕大多數(shù)的數(shù)據(jù)訪問層(DAL)需求,在你的應(yīng)用程序當(dāng)中,這可能并沒有顯著的的影響。 但是,如果您正在執(zhí)行大量的更新/插入/刪除操作,特別是大量的包含嵌套的實體,那么您可能需要自己寫代碼替代Repository的Save / Delete方法。.所有Save / Delete方法都是虛方法(virtual),因此您可以輕易重寫(override)他們。
另外請注意, RepositoryBase不支循環(huán)依賴(circular dependencies)的遞遞歸save 或者 delete操作。
結(jié)論
本文將和包括源代碼提供了一個簡單,方便和可擴(kuò)展的方式實現(xiàn)您的多層LINQ數(shù)據(jù)層CRUD(譯者注:增,刪,改,查)的方法。.它利用斷開模式,并支持保存(saving)和加載(loading)嵌套子實體(child entities).在Save 和Delete(譯者注:原文為Load,可能原作者筆誤)操作時候有一個小小的性能損失,但在性能至關(guān)重要的應(yīng)用中,您可以重寫這些Repositories類的Save和Delete。
對于一切,你安心上路,只需幾行代碼。
到此,相信大家對“LINQ to SQL如何實現(xiàn)數(shù)據(jù)訪問通用基類”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
網(wǎng)頁題目:LINQtoSQL如何實現(xiàn)數(shù)據(jù)訪問通用基類
轉(zhuǎn)載來于:http://www.chinadenli.net/article12/iijcgc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、全網(wǎng)營銷推廣、企業(yè)網(wǎng)站制作、網(wǎng)頁設(shè)計公司、商城網(wǎng)站、
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)