【发布时间】:2016-12-01 23:01:26
【问题描述】:
我在 SharePoint 中有一个 Web 部件,我正在尝试使用列表中特定字段的唯一/不同值填充下拉控件。
不幸的是,由于系统的性质,它是一个文本字段,因此没有其他确定的来源来获取数据值(即,如果它是一个选择字段,我可以获得字段定义并得到那里的值),并且我在后续的 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() 是否需要的问题:是的,需要。
SPListItemCollection 是 IEnumerable 但不是 IEnumerable<T>,所以我们需要强制转换才能完全使用 LINQ。
然后在它被转换为IEnumerable<SPListItem> 之后,SPListItem 是一个相当复杂的对象,我希望从该对象的 one 属性中找到不同的值。直接在IEnumerable<SPListItem> 上使用Distinct() 会产生......所有这些。所以我必须Select() 只是我想比较的单个值。
所以是的,Cast() 和 Select() 是绝对必要的。
正如 M.kazem Akhgary 在 cmets 中所述,在我的原始代码行中,每次调用 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 answer 中的 DataTable/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 转换方法。
【问题讨论】:
-
我认为那是因为
item[filterField.Id].ToString()部分。ToString方法是否被覆盖?如果不是,那么它基本上一遍又一遍地返回相同的字符串,你不会从散列中受益 -
不能将 Distinct 添加到检索中吗?
SPListItemCollection results = list.GetItems(query).Distinct()? -
.Cast.Select.Unique()linq 需要执行多长时间?
标签: c# performance linq sharepoint