天天看點

PetShop3.x學習筆記6-SQLServer學習筆記

一、 SQLServer主要功能:

1、              這是Microsoft SQL Server特定的PetShop DAL層實作,執行了IDAL接口定義的各方法

二、 實作細節:

1、              SQLHelper.cs檔案:就是MS DAAB

2、              主要定義了以下幾個方法對資料庫操作:

3、              ExecuteNonQuery()方法用于根據使用者的要求(SQL語句)對資料庫執行操作,這包括3個重載方法

4、              注意:ExecuteNonQuery()方法接受的SQL語句的參數,是一SqlParameter[]數組的形式出現的!!!這樣的設計方法,避免了由于不同SQL語句中的參數個數不同,而為每一個不同的SQL語句定義一個ExecuteNonQuery()方法的問題!!!而改由“SQL語句模闆”和“SqlParameter[]變量數組”自動進行變量對應!!!!!!

5、              ExecuteReader()方法根據使用者的要求(SQL語句)對資料庫執行查詢,并傳回給使用者一個包含特定查詢結果的SqlDataReader,供調用程式(使用者)使用

6、              ExecuteScalar()方法,用于根據使用者的要求(SQL語句)對資料庫執行查詢,并傳回查詢所傳回的結果集中第一行的第一列,而忽略額外的列或行,這包括兩個重載方法

7、              CacheParameters()方法,用于将SqlParameter[]數組存儲在Hashtable中,并用一個“鍵”進行辨別

8、              GetCachedParameters()方法,用于将CacheParameters()方法存儲在Hashtable中的SqlParameter[]數組,根據設定好的“鍵”進行提取

9、              Account.cs檔案:實作了IDAL中定義的方法

10、          以SignIn()方法為例,SignIn()方法的功能是解決使用者賬戶登入的整個問題,該方法中包含兩部分,一部分是邏輯判斷和操作,還有一部分是與資料庫互動。邏輯判斷部分一般以資料庫互動的結果為依據,給調用程式傳回合适的結果

11、          變量添加到SqlParameter[]數組的順序一定要和在SQL模闆中定義的變量的順序一緻!!!

12、          以SignIn()方法為例,示範SQLServer DAL實作在IDAL中定義的某一個方法過程:

(1)        BLL層調用IDAL接口層的Account類的SignIn()方法

(2)        IDAL接口實際是使用DAL層的Account類的SignIn()方法

(3)        由于Account類就是針對Account資料庫表進行操作的,是以在Account類當中,對Account資料庫表進行增删改查的SQL語句都已經寫成了模闆

(4)        在使用者(BLL層)調用SignIn()方法的時候,肯定提供了相應的變量,如使用者名UserId和密碼Password,這些都對應于SQL語句模闆當中需要的變量

(5)        為了滿足使用“SQLHelper類的方法”的格式,是以要把使用者傳入的變量(UserId和Password),也就是SQL語句模闆需要的變量,都包裝在一個SqlParameter[]數組當中,将所有參數作為一個數組傳遞給SQLHelper類的相應方法,進行操作

(6)        傳回一個新SqlParameter[]數組的方法也有講究!!!這裡在SignIn()方法中又調用了一個GetSignOnParameters()方法!!!

(7)        這個GetSignOnParameters()方法首先判斷,如果在SQLHelper.cs的Hashtable中存在(緩存了)一個“空的SqlParameter[]變量數組(也相當于模闆)”,則直接将此“空的SqlParameter[]變量數組(模闆)”傳回給調用程式(也就是SignIn()方法);相反,若不存在這樣一個“空的SqlParameter[]變量數組(模闆)”,則根據向SignOn表中插入資料的SQL語句模闆(對變量的需求),建立一個“空的SqlParameter[]變量數組(模闆)”,并緩存在SQLHelper的Hashtable當中,然後傳回給調用程式。

(8)        這裡有一個重要的内容!!!Hashtable parmCache究竟緩存的是什麼内容!!!實際上,parmCache緩存的是各“SQL語句模闆”對應(使用)的“空白SqlParameter[]變量數組(模闆)”!!!這個數組不是針對某個執行個體的(這裡的某個執行個體指的是某一套使用者名和密碼,如使用者名為A,密碼為B),而是空白的、通用的、針對某個“SQL語句模闆”的“SqlParameter[]變量數組(模闆)”!!!!!!

(9)        PetShop在這裡使用緩存參數數組模闆的方式,是為了提高性能,不必每次都為一個使用者的登入(每套使用者名和密碼執行個體)而建立一個新的“SqlParameter[]變量數組”

(10)    這裡終于了解了為什麼GetCacheParameter()方法,就算查找到有對應的“SqlParameter[]變量數組(模闆)”也不直接傳回,而是Clone一個之後再傳回!!!完全是因為在Hashtable中,我們需要存儲的隻是一個“SqlParameter[]變量數組(模闆)”,而不是一套使用者名和密碼的執行個體!!!如果直接傳回存儲在Hashtable中的“SqlParameter[]變量數組”,則在不同的使用者登入後,都會改寫模闆的内容,相當于将模闆執行個體化了,而這是錯誤的!!!

(11)    當SignIn()方法得到了一個空白的“SqlParameter[]變量數組”時,就根據調用SignIn()方法時提供的具體使用者名和密碼,初始化這個“SqlParameter[]變量數組”。而這個“SqlParameter[]變量數組”正是SQLHelper的ExecuteReader()方法需要的參數(類型)之一!!!

(12)    根據使用者登入動作時提供的使用者名和密碼資訊,在資料庫中去找對應的條目(預設使用者的輸入總是正确的)。如果找到了,則認為使用者登入是成功的,并且直接讀取使用者的其他個人資訊,執行個體化成為一個AccountInfo(在Model中定義)類型的對象,傳回給BLL調用程式!!!!!!

13、          GetAddress()方法的實作方式與SignIn()方法相同

14、          Insert()方法:用于向資料庫中插入一條新的使用者賬戶資訊

15、          由于需要将一個使用者注冊的資訊分别寫入3個資料庫表當中,為避免寫入的過程中發生問題,這裡使用一個事務,将3個寫入操作打成一個“包”——要麼全部執行成功,要麼一條都不執行!

16、          注意Transaction執行個體與conn對象的聯系——trans=conn.BeginTransaction();

17、          Inventory.cs檔案:對某個Item的庫存數量進行查詢和更改,其中更改主要指根據訂單的發貨量進行減少

18、          CurrentQtyInStock()方法沒有使用Hashtable緩存SQL語句的變量

19、          TakeStock()方法,傳入的參數是一個LineItemInfo[]數組,其中包含了某張訂單中使用者購買的所有物品。既然包括了不同的物品(Item),也就相應地有不同的ItemId,每個Item也有對應的購買數量Quantity

20、          實際上,需要為每一個Item(這裡的Item指的是LineItemInfo[]數組中的某一個元素!)數量的減少操作定義一個SQL語句,而這裡采用的是将對此張訂單中每一項需要購買的Item的數量的減少操作定義一個SQL語句,再将“根據所有要購買Item定義的SQL語句”“串聯”起來,中間用“分号”分隔,然後拿到資料庫中去執行!!!

21、          Item.cs檔案:主要完成對Item的兩個操作

22、          GetItemsByProduct()方法,根據傳入的productId,将此Product類别下的所有Item都選出來。注意,傳回的結果是一個IList接口的形式,可以根據此接口,使用index值枚舉ArrayList中的每個Item

23、          GetItem()方法很簡單,就是根據傳入的itemid值,傳回給調用程式相應的ItemInfo對象

24、          在Item.cs檔案的兩個方法中,沒有用到Hashtable緩存

25、          Order.cs檔案:

26、          第一個函數Insert()很特殊,值得研究一下代碼實作方式。Insert()方法實作的是向資料庫中增加一條訂單(Order)記錄,而增加一條訂單記錄需要向三個表當中寫入資料,一個是Orders表,它存儲了這個訂單的所有資訊;第二個是LineItem表,其中存儲了此訂單中,使用者訂購的所有物品(Item)及每種Item的數量;第三個是OrderStatus表,它存儲了訂單的狀态

27、          Insert()方法的整體思路也是逐漸建構SQL語句,并采用了與Inventory.cs檔案中TakeStock()方法相同的實作機制,将“向LineItem表插入某張訂單中使用者訂購的所有物品”的SQL語句,及其所需變量構造出來。這裡的SQL執行過程(插入)與TakeStock()方法(更新)類似

28、          在Insert()方法執行資料庫操作時,還提供了“檢錯”機制。所執行的SQL語句經過特殊的處理(定義了傳回@ID和@ERR),傳回參數兩個值,第一個是新插入Orders表的訂單的訂單ID号,第二個就是在SQL語句執行過程中的錯誤數量。如果出現錯誤,則抛出異常!

29、          第二個GetOrder()方法比較簡單,主要是根據傳入的Order号,傳回一個OrderInfo對象

30、          Product.cs檔案:實作兩個功能,一是根據給定的Category,将此Category下所有的Product傳回,形式為一個IList接口(ArrayList對象的引用?);第二個是一個具備“搜尋”功能的方法,根據傳入的關鍵字數組,傳回符合條件的Product ArrayList 接口IList

31、          GetProductsBySearch()方法,根據使用者提供的輸入字元串(數組),傳回給調用程式一個滿足要求的ArrayList。

32、          注意:GetProductsBySearch()方法使用了一個小技巧,拼接了包含查詢條件的SQL語句。在自己的項目中可以參考使用

33、          Profile.cs檔案:根據最感興趣的Category的内容,傳回一個BannerPath字元串

三、 啟發:

1、              在IDAL層和DAL層中定義的所有方法,都是為了支援“BLL層的需要”的!!!都是為了實作“BLL層的邏輯對資料庫進行操作的需要”而建立的!!!

2、              “一個資料庫表” 對應 “一個Model” 對應 “一個IDAL” 對應 “一個SQLServerDAL”

3、              軟體開發過程的問題:是否應先進行資料庫模組化,然後?有網友說是先規劃Model,然後設計資料庫

4、              由于對特定資料庫表進行操作的(增、删、改、查)SQL語句基本格式都是固定的,而針對不同的條目(情況),隻有個别的參數不同。是以在針對每個資料庫表編制的DAL層的類當中(如Account資料庫表對應的Account類),編寫好了針對目前資料庫表進行增删改查的SQL語句(模闆)。模版當中預留了變量的位置,用于處理不同的操作執行個體的條目,适應所有的情況。當然,對目前資料庫表沒有涉及到的操作,可以不進行定義!SQL語句模闆也要包括所有特定功能的增删改查操作!!!

5、              捋清了從“BLL層調用——具體資料庫操作”的整個設計和實作過程

6、              強調注意:在DAL層也是要分層的!!!直接操作ADO.NET元件,對資料庫進行增删改查的SQLHelper類(DAAB),又單獨被抽象出一小層,這樣解決了High Level 接口的問題!!!以上知識可以參考MSDN WebCast Modern C#的第8講

7、              由于第一遍看PetShop,是以重點看的是系統結構和設計思路。實作具體功能的代碼部分也做了簡單的研究,但是對于“為什麼使用這種方法實作”等問題并沒有搞清楚!這要等到自己動手做Demo系統的時候再仔細地分析

四、 問題:

1、              private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable());與直接private static Hashtable parmCache = new Hashtable());有何差別?也就是說對Hashtable進行同步的意義是什麼?

2、              SQLHelper.cs中的Hashtable parmCache的緩存範圍是什麼?

3、              為什麼隻有Account類中的SQL語句模闆的SqlParameter[]變量數組要緩存在Hashtable中,而Inventory類的卻不緩存呢?

4、              多條SQL語句,中間使用“分号”分隔,然後一起向資料庫執行,是否能成功?(為此,準備在本機資料庫中做試驗!!!結果:可以執行成功!!!)

5、              Hashtable的問題:在TakeStock()方法中,SQLHelper類的Hashtable的作用是什麼?由于沒有使用SQLHelper類對資料庫進行操作,而直接執行個體化了SqlCommand對象,是以就不涉及到“必須使用SqlParameter[]數組”的問題!

6、              還有,TakeStock()方法在執行資料庫更新的時候,為什麼不使用SQLHelper方法了?這完全可以應用啊!!!