2016-11-30 36 views
1

我在SharePoint中有一個Web部件,並且我嘗試使用列表中特定字段的唯一/不同值填充下拉控件。在LINQ中獲取不同值的更快方法?

不幸的是,由於系統的性質,它是一個文本字段,所以沒有其他確定的來源來獲取數據值(即,如果它是一個選擇字段,我可以獲得字段定義並且只是從那裏獲取值),並且我在隨後的CAML查詢中使用下拉列表中的所選值,因此值必須準確到列表項目上的值。目前該列表有arpprox。 4K項目,但它(並將繼續)增長緩慢。

而且,它是沙盒解決方案的一部分,所以它受到用戶代碼服務時間限制的限制 - 而且它往往會超時。在我的開發環境中,我在調試過程中逐步瞭解了代碼,看起來LINQ的那一行我實際上得到了不同的值,這是最耗時的,然後我完全調用了這個方法,超時停止,所以我相當肯定這是問題所在。

這裏是我的代碼:

private void AddUniqueValues(SPList list, SPField filterField, DropDownList dropDownControl) 
{ 
    SPQuery query = new SPQuery(); 
    query.ViewFields = string.Format("<FieldRef Name='{0}' />", filterField.InternalName); 
    query.ViewFieldsOnly = true; 

    SPListItemCollection results = list.GetItems(query); // retrieves ~4K items 

    List<string> uniqueValues = results.Cast<SPListItem>().Select(item => item[filterField.Id].ToString()).Distinct().ToList(); // this takes too long with 4K items 

    uniqueValues.Sort(); 

    dropDownControl.Items.AddRange(uniqueValues.Select(itm => new ListItem(itm)).ToArray()); 
} 

據我所知,有沒有辦法讓直接在CAML查詢「獨特」的價值觀,所以我怎麼能更快地做到這一點?有沒有一種方法來重構LINQ運行速度更快?

有沒有一種簡單/快速的方式從客戶端做到這一點? (REST會是首選,但如果需要,我會做JSOM)。


以爲我會在這裏添加一些額外的信息,因爲我做了一些進一步的測試並發現了一些有趣的結果。

首先,解決是否需要Cast()Select()的問題:是的,他們是。

SPListItemCollectionIEnumerable但不是IEnumerable<T>,所以我們需要強制轉換才能夠使用LINQ。

那麼它轉換爲IEnumerable<SPListItem>後,SPListItem是一個相當複雜的對象,我期待從剛一個該對象的屬性找到不同的值。直接在IEnumerable<SPListItem>上使用Distinct()可以得到所有這些。所以我必須Select()只是我想比較的單個值。

所以是的,Cast()Select()是絕對必要的。

正如M.kazem Akhgary在評論中指出的那樣,在我最初的代碼行中,每次調用ToString()(對於4K項)都會增加一些時間。但在測試一些其他的變化:

// original 
List<string> uniqueValues = results.Cast<SPListItem>().Select(item => item[filterField.Id].ToString()).Distinct().ToList(); 

// hash set alternative 
HashSet<object> items = new HashSet<object>(results.Cast<SPListItem>().Select(itm => itm[filterField.Id])); 

// don't call ToString(), just deal with base objects 
List<object> obs = results.Cast<SPListItem>().Select(itm => itm[filterField.Id]).Distinct().ToList(); 

// alternate LINQ syntax from Pieter_Daems answer, seems to remove the Cast() 
var things = (from SPListItem item in results select item[filterField.Id]).Distinct().ToList(); 

我發現所有的這些方法多個幾十秒完成。奇怪的是,從Pieter_Daems answerDataTable/DataView方法,而我平添了幾分提取我想要的值:

DataTable dt = results2.GetDataTable(); 
DataView vw = new DataView(dt); 
DataTable udt = vw.ToTable(true, filterField.InternalName); 
List<string> rowValues = new List<string>(); 
foreach (DataRow row in udt.Rows) 
{ 
    rowValues.Add(row[filterField.InternalName].ToString()); 
} 
rowValues.Sort(); 

只有1-2秒!最後,我打算使用Thriggle's answer,因爲它可以很好地處理SharePoint的5000個項目列表視圖閾值,我可能會在某一天處理這個閾值,而且它的速度只會稍微慢於(2-3秒) DataTable方法。還有很多,比所有LINQ快得多。

雖然有趣的是,從SPListItemCollection的特定字段獲取不同值的最快方式似乎是DataTable/DataView轉換方法。

+0

我認爲,這是因爲'項目[filterField.Id]的ToString()'一部分。 ToString方法是否被覆蓋?如果不是那麼它的基本上返回相同的字符串一遍又一遍,你不會從哈希中受益 –

+0

你不能添加區別到檢索? 'SPListItemCollection results = list.GetItems(query).Distinct()'? –

+0

'.Cast.Select.Unique()'linq需要執行多少時間? – TripleEEE

回答

2

你通過檢索所有項目首先檢查清晰度之前可能引入顯著延遲。

另一種方法是對SharePoint執行多個CAML查詢;這會導致每個唯一值有一個查詢(加上一個不返回結果的最終查詢)。

  1. 確保您的列表已將列索引應用於要枚舉其值的字段。
  2. 在您的初始CAML查詢中,排序,您想列舉的字段並強加行限制爲一個項目。
  3. 從該查詢返回的項目中獲取該字段的值,並將其添加到唯一值的集合中。
  4. 再次查詢列表,按字段排序並將行限制爲1,但是這次添加一個篩選器條件,以便它只檢索字段值大於剛剛檢測到的字段值的項目。
  5. 將返回項目中的字段值添加到唯一值集合中。
  6. 重複步驟4和5,直到查詢返回一個空的結果集,此時唯一值的集合應該包含該字段的所有當前值(假設從開始以來還沒有添加更多)。

這會更快嗎?這取決於您的數據以及重複值的發生頻率。

如果你有4000項,只有5個唯一值,你就可以收集這些5個值只有6輕巧CAML查詢,共有5個項目返回。這比查詢所有4000個項目更有意義,並逐個枚舉它們以查找唯一值。

在另一方面,如果你有4000項和3000個唯一值,你看查詢列表3001倍。這可能比檢索單個查詢中的所有項目並使用後處理來查找唯一值要慢。

+0

有趣的做法,我會試試看。 FWIW目前有28個獨特的價值觀,獨特的價值將被添加的速度將遠遠低於整體項目將增加的速度,所以我認爲這可能是一條路。 –

+1

如果您在SharePoint 2010或2013上,並且因此受限於列表視圖閾值(限制任何將返回5000個或更多項目的查詢),此方法也可能很有用。列索引在這種情況下尤爲重要。 – Thriggle

+1

是的,記住5K列表視圖閾值,我將使用這種技術,謝謝!它比我嘗試的LINQ方法快得多,但有趣的是不是最快的...(如果你感興趣,請參閱我更新的問題)。 –

0

Duplicate maybe?

.Distinct是O(n)的呼叫。 你不能比這更快。

這就是說,也許你想檢查你是否需要演員+選擇獲取唯一 - 我會嘗試一個HashSet。

2
var distinctItems = (from SPListItem item in items select item["EmployeeName"]).Distinct().ToArray(); 

或搜索結果轉換爲數據視圖,並做一些事情,如:

SPList oList = SPContext.Current.Web.Lists["ListName"]; 
SPQuery query = new SPQuery(); 
query.Query = "<OrderBy><FieldRef Name='Name' /></OrderBy>"; 
DataTable dtcamltest = oList.GetItems(query).GetDataTable(); 
DataView dtview = new DataView(dtcamltest); 
DataTable dtdistinct = dtview.ToTable(true, "Name"); 

來源:https://sharepoint.stackexchange.com/questions/77988/caml-query-on-sharepoint-list-without-duplicates

+0

@Pieter_Diems最終我和另外一個答案去,但值得注意的是,'數據表'/'DataView'轉換方法是最快的。如果您有興趣,請查看我更新的問題。 –