【转】4.2SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 2)
(三)使用SPQuery進行列表查詢
1、概述
列表查詢主要是指在一個指定的列表(或文檔庫)中按照某些篩選、排序條件進行查詢。列表查詢主要使用SPQuery對象,以及SPList的GetItems方法,將SPQuery作為參數傳遞,返回查詢到的列表條目集合,即SPListItemCollection類型。
在使用SPQuery進行列表查找的時候,其中一些屬性指定了其查找的特性:
Query屬性:通過該屬性指定CAML格式的篩選條件和排序條件(見上文),如果不指定,則默認返回范圍內的所有條目;
Folder屬性:通過該屬性指定在特定文件夾范圍內進行查找,如果不指定,則為列表或文檔庫的根文件夾;
RowLimit屬性:指定一次返回多少個條目,如果不指定,則返回所有條目;通過這個屬性與ListItemCollectionPosition屬性配合,可以實現列表條目的分頁查找;出于性能考慮,建議在一般情況下,都指定一個合理的RowLimit值,在SharePoint 2010中,系統也會自動對查詢返回結果的數量上進行一些限制和過濾(見后文關于查詢閾值的描述);
ViewFields屬性:指定CAML格式的返回字段(見上文),如果不指定,則默認返回所有字段;
ViewAttributes屬性:這個屬性主要和Folder屬性配合使用,用于指定查詢范圍,如果不指定,則查找指定文件夾下的條目和子文件夾(詳見后文)。
下面是一個列表查詢的例子:
1: using(SPSite site = new SPSite("http://sp2010/book")) 2: { 3: using(SPWeb web = site.OpenWeb()) 4: { 5: SPList list = web.Lists["Chapters"]; 6: SPQuery query = new SPQuery(); 7: query.Query = "<Where><Contains><FieldRef Name='Title'/>" + 8: "<Value Type='Text'>sp</Value></Contains></Where>" + 9: "<OrderBy><FieldRef Name='Created'/></OrderBy>"; 10: query.ViewFields = "<FieldRef Name='Title'/>"; 11: SPListItemCollection items = list.GetItems(query); 12: foreach(SPListItem itm in items) 13: Console.WriteLine(Convert.ToString(itm["Title"])); 14: } 15: }這是一個比較簡單的列表查詢,查詢了“Chapters”這個列表的根目錄中,“標題”字段包含字符串“sp”(SharePoint查詢中是否區分大小寫,由內容數據庫的相關規則指定,默認規則安裝的數據庫不區分字母的大小寫)的那些列表條目(可能還有文件夾),并輸出它們的標題。
?
2、查詢范圍(Scope)
通過配合使用SPQuery的Folder屬性和ViewAttributes屬性,可以控制查詢時候的范圍。
首先需要說明的是,在進行查詢的時候,普通列表條目(或者文檔庫中的文件)與文件夾是劃分查詢范圍的兩個重要條件。在查詢的時候,共有4種查詢范圍的設定:
- 不指定ViewAttributes,則查詢的是Folder對應的文件夾下面的列表條目(或文檔)以及子文件夾,就上面的例子來說,查詢的就是列表的根目錄,會返回根目錄中標題包含“sp”的列表條目,以及名稱包含“sp”的子文件夾。
- ViewAttributes = "Scope='FilesOnly'"。顧名思義,當指定了這個范圍之后,查詢的就是Folder對應文件夾下面的列表條目(或文檔)。如果把這個屬性加到上面的例子中,返回的將不再包括列表根目錄下的子文件夾——即使它們的名稱包含“sp”。
- ViewAttributes = "Scope='Recursive'"。當指定這個查詢范圍的時候,它查詢的范圍是Folder指定的文件夾及其子文件夾下的所有列表條目(或文檔),不管它們在哪個子文件夾下。
- ViewAttributes = "Scope='RecursiveAll'"。同樣的,它查詢的范圍是Folder指定的文件夾及其子文件夾下中的所有列表條目(或文檔)以及文件夾。
注意:當查詢結果同時包含了文件夾和普通條目(或文檔)的時候,不論排序條件是如何指定的,在返回的結果中必然是文件夾在前、普通條目(或文檔)在后。
?
3、分頁查詢
如果列表中一次查詢返回的結果非常非常多,勢必會造成一定的性能損失,通過RowLimit屬性可以在一定程度上控制性能的降低,但是僅通過RowLimit屬性,就只能返回查詢結果中的前若干條結果,如果用戶想要再查看后面的結果,就要配合使用到SPQuery的ListItemCollectionPosition屬性進行分頁查找。
在查找返回結果的SPListItemCollection對象中,有一個ListItemCollectionPosition屬性,包含了當前查詢結果的分頁信息。將這個屬性傳遞到下一次查找過程中使用的SPQuery的對應屬性中,就可以直接返回下一頁的查找結果,直到返回結果中的該屬性為null,就表示已經查找到了最后一頁。每頁包含多少個結果由RowLimit屬性來指定。
下面的程序就是在之前程序的基礎上,增加了分頁查找的例子:
1: using(SPSite site = new SPSite("http://sp2010/book")) 2: { 3: using(SPWeb web = site.OpenWeb()) 4: { 5: SPList list = web.Lists["Chapters"]; 6: SPQuery query = new SPQuery(); 7: query.Query = "<Where><Contains><FieldRef Name='Title'/>" + 8: "<Value Type='Text'>sp</Value></Contains></Where>" + 9: "<OrderBy><FieldRef Name='Created'/></OrderBy>"; 10: query.ViewFields = "<FieldRef Name='Title'/>"; 11: query.RowLimit = 5; 12: SPListItemCollection items = null; 13: int currentPage = 1; 14: do 15: { 16: items = list.GetItems(query); 17: Console.WriteLine(string.Format("Page {0}:", currentPage)); 18: foreach(SPListItem itm in items) 19: Console.WriteLine(Convert.ToString(itm["Title"])); 20: query.ListItemCollectionPostion = 21: items.ListItemCollectionPosition; 22: currentPage++; 23: }while(items.ListItemCollectionPosition != null); 24: } 25: }細心的讀者可能會注意到SharePoint內置的列表視圖在進行分頁的時候,只有上一頁和下一頁的鏈接按鈕,而沒有辦法直接跳轉到第x頁,在翻到最后一頁之前,也沒有辦法知道在這個視圖下一共有多少個條目。在經過上面的介紹之后,現在您應當已經能想到這個現象背后的原因了。SharePoint內置提供了如上面程序那樣向后翻頁的機制,讀者可以在前后翻頁的時候注意一下Url地址的變化,自行思考如何來實現“上一頁”的效果。實際上,在SharePoint 2003的時代,列表視圖里面就只有“下一頁”的按鈕,而沒有“上一頁”的按鈕。
?
4、通過查閱項進行列表關聯查詢
在進行列表查詢的時候,使用之前介紹的CAML語法,可以很容易地在一個列表中根據其中字段的不同篩選條件進行查詢。但是從應用的角度來講,僅將查詢局限在一個表中是遠遠不夠的,我們在開發傳統應用的時候必然會遇到多個表之間進行關聯查詢的場景。這里我們就將介紹,在SharePoint中,如何實現這種多表之間關聯的查詢。
為了能夠更加清楚的說明問題,在這里我們不妨假定一個經過簡化后的實際場景:一個軟件銷售公司擁有自己的銷售人員列表,其中包含了銷售人員的姓名、擅長的銷售領域等等;同時也擁有一張訂單列表,包含了所銷售的產品的名稱、數量、單價,以及這張訂單的銷售人員的姓名。于是我們不難設計出如下的數據表結構:
在SharePoint中實現這樣的數據結構,我們可以想到創建“Sales”和“Orders”這兩個列表,并在“Orders”列表中創建一個“Saler”查閱項來查閱“Sales”列表的姓名字段,如下圖:
?
?
此時,如果我們需要查找“張三”這個銷售賣出去的所有訂單,可以使用如下的CAML:
1: <Where><Eq> 2: <FieldRef Name='Saler' /> <!--注意這里使用了字段內部名稱 --> 3: <Value Type='Lookup'>張三</Value> 4: </Eq></Where>或者更準確的(考慮到可能有多個名叫“張三”的銷售):
1: <Where><Eq> 2: <FieldRef Name='Saler' LookupId='TRUE' /> 3: <Value Type='Lookup'>1</Value> <!--1是我們關心的那個“張三”的ID--> 4: </Eq></Where>于是,很自然地我們通過查閱項實現了最簡單的兩個列表關聯查詢。
直到有一天,老板希望能夠查看一下那些專長領域是Office的銷售都賣出過哪些訂單。按照一般的思路,我們可以先在銷售列表中找到那些專長領域是Office的銷售有哪些,然后再分多次去訂單列表中查詢,最后把查詢結果拼在一起。但是有沒有更簡單高效的方法呢?在SharePoint 2010中,為此提供了兩種方式:
方法一:使用SharePoint 2010查閱項新增的映射欄(Project Fields)的功能,將姓名、領域兩個字段同時映射到銷售列表中,見下圖:
?
?
于是,我們就可以使用映射到銷售列表中的“Sales:領域”這一字段進行查詢了:
1: <Where><Eq> 2: <FieldRef Name='SalerField' /> <!--依然是字段的內部名稱 --> 3: <Value Type='Lookup'>Office</Value> 4: </Eq></Where>方法二:使用SharePoint 2010增加的SPQuery的列表關聯查詢功能。
這里,我們需要先借助SPQuery的Joins屬性聲明兩個列表的關聯,再使用ProjectedFields屬性創建一個虛擬的映射字段,在這個虛擬字段上使用CAML進行查詢。SPQuery的創建過程如下:
1: SPQuery query = new SPQuery(); 2:? 3: query.Joins = @"<Join Type='LEFT' ListAlias='Sales'> 4: <Eq> 5: <FieldRef Name='Saler' RefType='ID'/> 6: <FieldRef List='Sales' Name='ID'/> 7: </Eq> 8: </Join>"; 9:? 10: query.ProjectedFields = @"<Field Name='SalerField' Type='Lookup' 11: List='Sales' ShowField='Field'/>"; 12:? 13: query.Query = @"<Where><Eq> 14: <FieldRef Name='SalerField'/> 15: <Value Type='Lookup'>Office</Value> 16: </Eq></Where>";在這段程序中,首先我們創建了一個列表關聯(Join),其類型是左外連接(Type=LEFT),并將這個關聯賦予一個列表別名(ListAlias='Sales'),關聯是建立在當前列表(即訂單列表)的“Saler”字段(查閱項,本質是查閱ID)與Sales列表(這個名稱必須與Join中聲明的列表別名相同)的ID字段。實際上,這一段CAML如果“翻譯”成SQL語句的話(這里只是為了能夠讓傳統的開發人員更容易理解這段CAML,并不表示SharePoint實際存儲的數據表結構。),看上去就變得熟悉多了:
1: Orders LEFT JOIN Sales 2: ON 3: Orders.Saler = Sales.ID接下來,為了能夠在查詢條件中使用“領域”這一字段,需要把銷售表中的“領域”映射為訂單表中的一個虛擬字段(ProjectedField),并給這個字段隨意指定一個名稱(比如程序中的“SalerField”。該字段來自Sales列表(List='Sales',同樣要求與Join中定義的的列表別名相同)的“Field”字段(ShowField='Field',“領域”的內部名稱),類型為查閱項(Type='Lookup')。
之后,我們就可以把這個映射字段“SalerField”當作訂單列表中的字段在查詢使用了。
這兩種方法所達到的效果是完全一樣的,區別只是在于創建一個真正的映射字段,還是創建一個虛擬的映射字段。
由于我們的程序工作良好,為公司提高了效率,公司不斷壯大發展,在其它城市也成立了分部。此時,為了能夠管理其他城市的銷售人員,我們的數據結構就要發生變動了,需要引入一張新的“城市”列表,并且在銷售列表上為每個銷售附加上城市的信息:
于是,在SharePoint中,我們也相應的創建了一個城市列表,并且在銷售列表中創建了一個引用了城市列表的查閱項:
這個時候,老板必定會希望能夠查看一下某個特定城市的所有訂單的銷售情況。當然,我們仍然可以先找到這個城市的所有銷售,然后分別查找出他們的訂單并把結果合并起來。不過在SharePoint 2010中,仍然有更直接的方法,那就是使用列表的關聯查詢。
遺憾的是,SharePoint的查閱項在映射多個列的時候,無法再從中選擇一個查閱項,因此我們只能通過方法二中的技術來實現這一需求。這個SPQuery的創建程序片段如下:
1: SPQuery query = new SPQuery(); 2:? 3: query.Joins = @"<Join Type='LEFT' ListAlias='Sales'> 4: <Eq> 5: <FieldRef Name='Saler' RefType='Id' /> 6: <FieldRef List='Sales' Name='ID' /> 7: </Eq> 8: </Join> 9: <Join Type='LEFT' ListAlias='Cities'> 10: <Eq> 11: <FieldRef List='Sales' Name='City' RefType='Id' /> 12: <FieldRef List='Cities' Name='ID' /> 13: </Eq> 14: </Join>"; 15:? 16: query.ProjectedFields = @"<Field Name='SaleCity' Type='Lookup' 17: List='Cities' ShowField='Title'/>"; 18:? 19: query.Query = @"<Where> 20: <Eq> 21: <FieldRef Name='SaleCity' /> 22: <Value Type='Text'>北京</Value> 23: </Eq> 24: </Where>";?
?
?
與之前的程序相比,這段程序只是多做了一次Join,將關聯的列表從兩個變成了三個,它“相當于”如下的SQL語句(同樣,這里只是為了方便傳統的開發人員能夠理解SharePoint中的Join的寫法):
1: Orders LEFT JOIN Sales 2: ON 3: Orders.Saler = Sales.ID 4: LEFT JOIN Cities 5: ON 6: Sales.City = City.ID程序的其他部分就不再贅述了。
我們可以看到,在SharePoint 2010中,列表查閱項的功能已經有了極大的增強,這代表著今天實現列表與列表之間的關聯越來越方便,可以更便捷地搭建出我們所需的應用。不過也應當注意到,隨著關聯的日趨復雜,CAML的編寫也跟著越來越復雜,即使是很有經驗的程序員也難免在這個地方出現一些手誤,而這種錯誤在程序調試過程中又是很難發現的。好在SharePoint 2010為我們提供了一項新的查詢技術,我們將在下一節中對其進行詳細介紹。
?
5、列表查詢的注意事項
在進行列表查詢的時候,有一點是特別需要注意的,那就是在進行不同列表的不同查詢條件的查詢情況下,必須每次重新構造SPQuery對象。換句話說,每做一次查詢(只要查詢條件不同),就要重新new一個SPQuery對象出來。否則,在程序運行的時候,仍會以最初的條件進行查詢。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【转】4.2SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 rqt_console 和 ros
- 下一篇: 浦发淘票票信用卡申请进度查询