更快地在LINQ中获取不同的值?

时间:2016-11-30 14:32:11

标签: c# performance linq sharepoint

我在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()的问题:是的,它们是。

SPListItemCollectionIEnumerable但不是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();

我发现所有这些方法都需要数十秒才能完成。奇怪的是,来自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转换方法。

3 个答案:

答案 0 :(得分:2)

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

或者将结果转换为DataView并执行以下操作:

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

答案 1 :(得分:2)

在检查清晰度之前,您可能会先检索所有项目,从而引发重大延迟。

另一种方法是针对SharePoint执行多个CAML查询;这将导致每个唯一值一个查询(加上一个不返回结果的最终查询)。

  1. 确保您的列表已将列索引应用于要枚举其值的字段。
  2. 在您的初始CAML查询中,排序按您想要枚举的字段并强加行限制为一个项。
  3. 从该查询返回的项目中获取该字段的值,并将其添加到您的唯一值集合中。
  4. 再次查询列表,按字段排序并将行限制设为1,但这次添加过滤器条件,以便它只检索字段值大于字段值的项目你刚才发现了。
  5. 将返回项目中字段的值添加到您的唯一值集合中。
  6. 重复步骤4和5,直到查询返回空结果集,此时您的唯一值集合应包含该字段的所有当前值(假设自您开始以来尚未添加更多)。
  7. 这会更快吗?这取决于您的数据以及重复值的出现频率。

    如果您有4000个项目且只有5个唯一值,则您只能在6个轻量级CAML查询中收集这5个值,总共返回5个项目。这比查询所有4000个项目并通过它们一次枚举一次来查找唯一值更有意义。

    另一方面,如果您有4000个项目和3000个唯一值,那么您正在查询列表3001次。这可能比检索单个查询中的所有项目并使用后处理来查找唯一值要慢。

答案 2 :(得分:0)

Duplicate maybe?

  

.Distinct是一个O(n)电话。    你不能比这更快。

这就是说,也许你想检查你是否需要使用强制转换+选择来获取独特的东西 - 我会尝试使用HashSet。