我有一个指向 100,000 条记录 SQL 表的 SharePoint 外部列表。我必须在读取列表操作上设置过滤器,否则列表不起作用。它将在尝试返回完整列表时超时。所以我在操作中添加了一个大小为 200 的限制过滤器。
这导致的问题是,当我使用 CAML 查询外部列表时,它只搜索返回的 200 个条目,而不是完整列表。
我希望它搜索整个列表,但最多只返回 200 个匹配条目。
我怎样才能最好地做到这一点?
我有一个指向 100,000 条记录 SQL 表的 SharePoint 外部列表。我必须在读取列表操作上设置过滤器,否则列表不起作用。它将在尝试返回完整列表时超时。所以我在操作中添加了一个大小为 200 的限制过滤器。
这导致的问题是,当我使用 CAML 查询外部列表时,它只搜索返回的 200 个条目,而不是完整列表。
我希望它搜索整个列表,但最多只返回 200 个匹配条目。
我怎样才能最好地做到这一点?
也许 SharePoint-Exchange 的这个答案可以帮助你。 https://sharepoint.stackexchange.com/questions/31566/caml-and-external-list-pass-parameter-to-readlist-finder-method
我的想法是,您可能需要修改您的 readlist-method 以确保它读取整个 SQL 表,但使用 readlist-method 中的 filter 参数指定的 Where 参数。就像是
伪代码:
public static IEnumerable<YourEntity> ReadList(string param)
{
if(string.IsNullOrEmpty(param) == true)
{
//Your original code thata only fetches the first 200 items
}
else
{
//Read your SQL-table with a Where ParamName = 'param' - clause
}
}
祝你好运
根据您的查询结构和此处提供的信息,报告表明 <RowLimit>
实现了您想要的功能:
RowLimit元素设置视图的行限制。
句法
属性
- 分页:可选布尔值。如果列表支持逐页显示更多项目,则为 TRUE。如果为 FALSE 或未指定,则行限制是绝对的,并且没有查看更多项目的链接。
注意事项:请注意文档中的备注。
您可能已经出于您的目的对此进行了检查(引用您的问题:“所以我在操作中添加了一个大小为 200 的限制过滤器。”)。那么,进入下一个问题:
这导致的问题是,当我使用 CAML 查询外部列表时,它只搜索返回的 200 个条目,而不是完整列表。
这似乎很奇怪。如果您确实在使用<RowLimit>
并且文档正确:
RowLimit 元素设置视图的行限制。
并且:
该
<RowLimit>
标记位于视图的架构定义中( 的直接子级),因此不能嵌套在<Query>
标记内。
然后,您的嵌套查询应该在您的视图组件之前执行以满足您的限制语句的保证。作为推论,这应该允许您在查询定义的其余集合中执行结果分页。
基于这些原则,我们可能会构建一个这样的分页查询:
<View>
<RowLimit Paged='True'>200</RowLimit>
<Method Name='ReadList'/>
<Query>
<Where>
<Contains>
<FieldRef Name='Name'/>
<Value Type='Text'>{0}</Value>
</Contains>
</Where>
</Query>
<ViewFields>
<FieldRef Name='Name'/>
<FieldRef Name='Id'/>
<FieldRef Name='BdcIdentity'/>
</ViewFields>
</View>
请注意,如文档中所述,我们必须实现<PagedRowset>
. 如果这不是我们想要的,我们在Paged='FALSE'
上面设置。
我可能完全不在这里,因为这似乎正是您已经尝试过的。但是,为了详尽地规划空间,建议它不会有坏处。
不幸的是,这是查询外部列表的一个已知问题。但是,在 OOB Web 部件中,通过 XSLT 支持分页。RowLimit 仅适用于 XSLT 而不适用于 CAML 查询。在外部列表中,没有服务器端分页,而是客户端分页,这意味着 SharePoint 会提取所有数据,然后在视图中设置筛选器限制。
这是无代码 BCS 本身并不能真正削减它的场景之一。要么将其作为数据库服务器上的存储过程来实现,要么使用 Visual Studio 中内置的自定义 BDC 连接器。
如果您无法将整个 SQL 表拉入外部列表,那么您将无法像 SharePoint 列表那样查询该数据集。
但是,我可以提供一个解决方案,我们已将其用于与此几乎相同的场景,并且对我们来说效果非常好。在我们的场景中,我们正在查询一个需要永远返回大型数据集的 Oracle 数据库。
我们采用的方法是使用工厂模式来确定应以何种方式查询数据源(SharePoint 列表、外部数据库等)。
下面的例子有点陈词滥调,但它们很好地说明了这个概念。
因此,从定义如何查询数据集以及将返回哪些字段的接口开始:
public interface IQueryData
{
string ListToQuery { get; set; }
List<MyResultObject> ExecuteQuery();
}
您将有一个自定义对象,该对象代表查询返回的单个记录
public class MyResultObject
{
public string FileRef { get; }
public string Title { get; set; }
// any other fields you'd like to see potentially returned...
}
然后你会有一个数据提供者为 SQL 数据源实现这个接口
public class SqlDataProvider : IQueryData
{
public string ListToQuery { get { return "BigSqlTable"; } }
public List<MyResultObject> ExecuteQuery()
{
// query your external data source here...
// populate a list of MyResultObject's from the result set and return it to the consumer
}
}
您还有一个数据提供者,它实现了 SharePoint 数据源的接口
public class SharePointDataProvider : IQueryData
{
public string ListToQuery { get { return "MySharePointList"; } }
public List<MyResultObject> ExecuteQuery()
{
// query your SharePoint list here, using CAML, SharePoint object model, etc...
// populate a list of MyResultObject's from the result set and return it to the consumer
}
}
通过此实现,您已将查询的逻辑和详细信息封装在各自的数据提供程序中。
现在您将拥有一个构建适当数据提供程序的工厂(基于指定的 ListToQuery 参数):
public static class QueryDataProviderFactory
{
public static IQueryData Build(string listToQuery)
{
switch(listToQuery)
{
case "BigSqlTable": return new SqlDataProvider(); break;
case "MySharePointList": return new SharePointDataProvider(); break;
// you can have many other implementations here that query your data sources in different manners
}
}
}
最后,您将使用您的工厂来启动您的查询,传入您要查询的数据源的名称:
public List<MyResultObject> RunQuery()
{
return QueryDataProviderFactory.Build("BigSqlTable").ExecuteQuery();
}
这种模式将您的外部实现封装到它自己的数据提供者中,并从消费者那里抽象出查询的细节。消费者需要做的就是指定他们想要查询的列表的名称,工厂决定启动哪个实现。
您甚至可以使您的 IQueryData 接口实现泛型以进一步扩展:
public interface IQueryData<T>
{
string ListToQuery { get; set; }
List<T> ExecuteQuery();
}
这将为消费者打开大门,让他们也可以指定他们期望返回的对象类型。
我们的查询数据接口实际上有更多的成员,它们为我们的查询提供程序添加了更多的扩展点,但我认为这个示例以简洁易懂的方式说明了这一点。
只是想提供这个建议,因为它看起来几乎与我们一年前遇到的情况相同,而且这种策略对我们来说效果很好。
使用来自SPQuery和SPListItemCollection的属性 ListItemCollectionPosition例如
using (var web = site.OpenWeb("bla-bla"))
{
var list = web.Lists["your_list"];
var query = new SPQuery();
query.Query = "your query";
do
{
var items = list.GetItems(query);
foreach(SPListItem item in items)
{
//your code
}
query.ListItemCollectionPosition = items.ListItemCollectionPosition;
} while(query.ListItemCollectionPosition != null);
}