我在 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
是一个相当复杂的对象,我希望从该对象的一个属性中找到不同的值。使用Distinct()
直接上IEnumerable<SPListItem>
产量..全部。所以我必须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();
我发现所有这些方法都需要几十秒才能完成。奇怪的是,DataTable
/DataView
方法来自 Pieter_Daems answer ,我添加了一些内容来提取我想要的值:
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 项 ListView 阈值,我可能有一天会处理该阈值,并且它仅比 DataTable
慢一点(2-3 秒)。方法。仍然比所有 LINQ 快得多。
有趣的是,尽管如此,从 SPListItemCollection
中的特定字段获取不同值的最快方法是似乎是DataTable
/DataView
转换方法。
最佳答案
在检查唯一性之前先检索所有项目可能会导致严重延迟。
另一种方法是针对 SharePoint 执行多个 CAML 查询;这将导致每个唯一值进行一次查询(加上一个不返回结果的最终查询)。
- 确保您的列表已将列索引应用于您要枚举其值的字段。
- 在初始 CAML 查询中,按您要枚举的字段排序,并施加一个项目的行限制。
- 从该查询返回的项目中获取字段值,并将其添加到您的唯一值集合中。
- 再次查询列表,按字段排序并施加行数限制为 1,但这次添加过滤条件,以便仅检索字段值大于字段值的项目您刚刚检测到。
- 将返回项目中的字段值添加到您的唯一值集合中。
- 重复步骤 4 和 5,直到查询返回空结果集,此时您的唯一值集合应包含该字段的所有当前值(假设自开始以来尚未添加更多值)。
这样会更快吗?这取决于您的数据以及重复值发生的频率。
如果您有 4000 个项目,但只有 5 个唯一值,则只需 6 个轻量级 CAML 查询即可收集这 5 个值,总共返回 5 个项目。这比查询所有 4000 个项目并一次枚举一个来查找唯一值更有意义。
另一方面,如果您有 4000 个项目和 3000 个唯一值,则需要查询列表 3001 次。这可能比检索单个查询中的所有项目并使用后处理来查找唯一值要慢。
关于c# - 在 LINQ 中获取不同值的更快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40890979/