溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

LINQ to SQL如何實現數據訪問通用基類

發布時間:2021-07-12 16:31:11 來源:億速云 閱讀:194 作者:chen 欄目:編程語言

本篇內容主要講解“LINQ to SQL如何實現數據訪問通用基類”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“LINQ to SQL如何實現數據訪問通用基類”吧!

不幸的是,當你對LINQ進行仔細研究后,我發現在多層架構中使用LINQ的并不是十分容易。

本文介紹用LINQ to SQL實現數據層的典型的問題點 ,并提供了一個簡單,方便和靈活的方式來克服它們。

本文附帶的LINQ to SQL 實現數據訪問通用類有以下的特點:

實現了存儲庫模式,你可以用不到10行代碼執行LINQ實體類型的CRUD (Create, Update, Delete)操作。
無縫協作,支持LINQ斷開模式(Disconnected LINQ Mode)。
在單一數據庫和LINQ實體間支持透明的數據庫更新和數據加載。
提供為一種方便的功能,在調試你的應用程尋時候,它把所有執行的SQL語句輸出控制臺。
本文將假定您對LINQ to SQL (也稱為DLINQ )有一個基本的了解并如何使用它。否則,,,回到此網頁,看看本教程入門系列,如何在多層次應用中使用LINQ to SQL。

存在的問題

如果您只是在你的UI層直接用LinqToDataSource對象銜接數據庫,那LINQ to SQL太容易使用了。但是,這種做法不完全面向對象,當然也不是一個可取的架構,除非你是為了快速編碼和臟亂的應用程序,并且最終沒有去擴展的它打算。

相反,大多數開發人員把它們的應用程序劃分成若干層,如下:

數據訪問層(Data Access Layer)

業務層 (Business Layer)

用戶界面層(UI Layer)

這就是所謂的多層數據庫應用程序設計。LINQ to SQL將用于數據訪問層。

LINQ to SQL的問題是-盡管它的許多優點-但是如果要實現數據層并不是很簡單。

請看下面的數據庫模式(database schema):

一旦你要加載和保存LINQ實體到同一個的數據上下文實例(data context instance)(這就是所謂“連接模式”),用LINQ實現數據層非常直接。

例如,讓我們從數據庫中獲取實體編號為1的客戶,改變屬性first name為“Homer”后在重新儲存到數據庫中。在一個多層數據庫應用程序中,在UI或業務層的某個地方的代碼可能看起來就像這樣:

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);

最簡單的方法來實現上面使用到的數據層加載和保存功能是:

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模式:數據上下文(data context)在當前作用域一直有效(譯者注:一直保持連接狀態),所以在把實體保存到數據庫的時候,它總是可以重復使用。其中仍然連接到它。

當然,這種做法方便并且在上述的單個例子中能運行,但它存在嚴重的并發問題,因為一個數據庫方面是用于所有數據庫操作。

當調用方法Save(),bmitChanges提交的不僅僅是當前Save 方法參數相關的LINQ實體,還包括所有改變了的實體。

但是及時把這個缺陷考慮在一邊,使用LINQ在一個多層ASP.NET應用程序中,您還不能以相同方式實現數據層。首先,可能要求是這樣,在一個頁面請求中,LINQ實體被加載,然后在下一個頁面請求中,它更新并儲存到數據庫中的。.同時,您的原始數據上下文在當前作用域內已經無效的(譯者?。篐TTP協議是無狀態的),造成的您的LINQ實體游離。

還有許多其他情況下你需要使用斷開LINQ模式:例如您實現的數據庫層可能要作為一個Web服務,提交(commit)以前序列化LINQ實體到數據庫等等。

用斷開模式的LINQ to SQL實現數據訪問層

所以,在斷開的LINQ模式下,我們如何實現數據層的Save( )方法?

我們必須

Detach the entity from the old data context從舊的數據上下文中分離實體

Create a new data context創建一個新的數據上下文

Attach the entity to the new context附加實體到新的數據上下文

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.   }

現在只要你喜歡,您可以加載修改任意多實體,并且只提交他們一部分到數據庫。但由于使用斷開的LINQ ,這個程序并不會感知到LINQ實體之間的關系。

例如,假設在業務層或用戶界面層您要做到以下幾點:

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。為了做到這一點,我們還需要附加或插入遞歸所有相關的子實體(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.   }

不是很復雜,但很多打字(譯者注:翻譯不是很難,但要一句句的理解,還要打很多字)。并且這只是支持一個微不足道的database scheme和一個單一的實體類型。.想象一下,如果實現數據庫層有幾十個實體類型與幾十個外鍵關系,在這個數據存儲類中,你將要為每一個LINQ實體寫幾十套foreach循環,這不僅是單調乏味,而且還容易出錯。.當你添加新的表,你必須添加幾十foreach循環。

如何避免這些問題

在相當多的在線調研后,我實現了一個RepositoryBase類,使用他您可以快速實現您的數據層,所示為測試通過的例子。 首先,用對象關系映射器(譯者注:Visual Studio自帶工具)來產生序列化的LINQ實體:在Visual Studio中打開dbml文件,在空白區域某處左鍵單擊,彈出屬性窗口,設置“Serialization Mode屬性”為“Unidirectional”。

LINQ to SQL如何實現數據訪問通用基類

現在您可以繼承RepositoryBase實現您自己的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.   }

您的每一個實體的類型都照這樣做,你就擁有了一個工作在斷開模式下無縫數據層。您繼承Repository的類自動執行下列方法:

LINQ to SQL如何實現數據訪問通用基類


作為錦上添花的功能,在應用程序調試的過程中,你還可以通過輸出控制臺看到執行對數據庫的操作的SQL命令。這多虧了被用于RepositoryBase的SQL調試輸出的Kris Vandermotten 方便的DebuggerWriter組件(譯者注:外國人就是紳士)!

天下有沒有免費的午餐...

當前的加載(Load)操作中,沒有任何顯著的性能損失,但是當你調用Save or Delete方法時候,幕后用到一點反射(reflection)操作。

對于絕大多數的數據訪問層(DAL)需求,在你的應用程序當中,這可能并沒有顯著的的影響。 但是,如果您正在執行大量的更新/插入/刪除操作,特別是大量的包含嵌套的實體,那么您可能需要自己寫代碼替代Repository的Save / Delete方法。.所有Save / Delete方法都是虛方法(virtual),因此您可以輕易重寫(override)他們。

另外請注意, RepositoryBase不支循環依賴(circular dependencies)的遞遞歸save 或者 delete操作。

結論

本文將和包括源代碼提供了一個簡單,方便和可擴展的方式實現您的多層LINQ數據層CRUD(譯者注:增,刪,改,查)的方法。.它利用斷開模式,并支持保存(saving)和加載(loading)嵌套子實體(child entities).在Save 和Delete(譯者注:原文為Load,可能原作者筆誤)操作時候有一個小小的性能損失,但在性能至關重要的應用中,您可以重寫這些Repositories類的Save和Delete。

對于一切,你安心上路,只需幾行代碼。

到此,相信大家對“LINQ to SQL如何實現數據訪問通用基類”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女