嗯…事先聲明,其實這篇文章沒有太多實際的使用意義,所以想了解某個東東怎麼用的同學可以按Alt + F4(或者Ctrl + W)了。想了解SharePoint裡面是怎麼運作的同學可以繼續往下翻。
最近正在和KB一起寫關於SharePoint 2010開發方面的一本書,在研究2010新增加的物件模型的時候,偶然發現了這個方法。我們都知道在2003/2007裡面,根據ID取得清單條目使用的是SPList的GetItemById方法(什麼,沒聽過這個方法?那恐怕你不是一個合格的SharePoint開發人員…)。新增加的這個方法名字叫GetItemByIdSelectedFields(同時也增加了一個GetItemByIdAllFields的方法與之作伴,不過這個和GetItemById是完全等效的,就不再廢話了),方法的定義是這樣的:
1: public SPListItem GetItemByIdSelectedFields(int id, params string[] fields)
當我第一眼看到這個的時候,立刻就想到了SPQuery的那個ViewFields屬性,獲取某個列表條目的時候,只返回某些指定的字段,來提高效率。可是當我寫了一個Console程式試驗的時候,發現並不是我想像中的樣子,例如我寫成(這個方法要求寫內部名稱):
1: SPListItem item = spList.GetItemByIdSelectedFields(1, "Title", "Created"); 2: Console.WriteLine(item["Modified"]);
這段程式居然沒有報錯,而且Modified的值也正常返回了,於是我試了試,一個自訂列表裡面居然有50來個字段的值都正常返回了,但是所有的查閱項目、用戶和用戶組(其實這個本質上也是查閱項)都沒有返回。
在好奇驅使下(暫時還害不死我),我Reflector了一下這個方法的原始碼:
1: if(field == null) 2: { 3: throw new ArgumentNullException("fields"); 4: } 5: 6: StringBuilder builder = new StringBuilder(); 7: foreach (string str in fields) 8: { 9: if (str != null) 10: { 11: builder.Append("<FieldRef Name="" + str + ""/>"); 12: } 13: } 14: 15: foreach (SPField field in this.Fields) 16: { 17: bool flag = false; 18: foreach (string str2 in fields) 19: { 20: if (str2 == field.InternalName) 21: { 22: flag = true; 23: break; 24: } 25: } 26: if (!flag && field.MustFetchByDefault) 27: { 28: builder.Append("<FieldRef Name=""); 29: builder.Append(field.InternalName);300 : builder.Append(""/>"); 31: } 32: } 33: 34: return this.GetItemById(id, null, false, builder.ToString());
關於最後那個GetItemById是怎麼回事,暫時先不用再去深究了,只要知道它是一個GetItemById的重載,目的就是查找條目用的就行了,最後一個參數把需要獲取的字段以CAML的形式放進去。
第7行那個foreach很好理解,把我們需要的字段加進去;但是第15行的那個foreach一開始就有點讓人摸不著頭腦了,還要把其他字段也放進去?而且SPField的這個MustFetchByDefault是什麼東西?再挖挖看看:
1: internal bool MustFetchByDefault 2: { 3: get 4: { 5: string fieldAttributeValue = this.GetFieldAttributeValue("List"); 6: if(!string.IsNullOrEmpty(pfieldAttrib Global's) & 6: ToString())) 8: { 9: return false; 10: } 11: return true; 12: } 13: }
如何判斷一個欄位是不是要取呢?透過判斷欄位的一個List屬性,至於GetFieldAttributeValue方法就不再往上貼了(否則有騙字數的嫌疑),總之它是從Field的類似SchemaXml屬性(字段描述)的Xml結點中,去找一個List的屬性。如果找到了,而且不是GlobalList.Docs(某個特殊的東東)的話,那麼這個欄位就不是必須的,換句話說這個欄位我就不用回傳給使用者。
那什麼欄位的SchemaXml裡會有List屬性?一個字段裡有一個和列表的屬性?查閱項!哈,真的是迴避掉了所有的查閱項。 (Docs這個東西是「路徑」這個欄位的List屬性,估計有某些特殊的來源)
現在我們知道為什麼會包含其他所有字段,並且不包含查閱項了。但為什麼要這樣?如果我們對SharePoint的內容資料庫有所了解的話,我們會知道其實查閱項目在內容資料庫裡只存了一個ID值在AllUserData表裡面(但是用物件模型取出來的時候,是包含查閱那個條目對應欄位的內容的),這也意味著,如果要返回查閱項目的值,就需要多做一些額外的資料庫操作(例如再去找到被查閱的那個條目,把相應字段的值返回來,拼裝成“1 ;#Administrator「這種鬼樣子)。更重要的是,如果這個查閱項是一個多值的,那麼這個查閱項本身都是保存在另外一個表中的(AllUserDataJunctions),這樣要返回起來還真是要費不少功夫。所以2010裡面新增加了這麼一個東西,如果我們的清單中包含好多個查閱項,而我們可能暫時只用到其中一兩個(或者一個都不用)的話,看來用這個方法確實能提高不少效率。