【问题标题】:Query large external list with CAML使用 CAML 查询大型外部列表
【发布时间】:2011-08-25 04:37:19
【问题描述】:

我有一个指向 100,000 条记录的 SQL 表的 SharePoint 外部列表。我必须在读取列表操作上设置过滤器,否则列表不起作用。它将在尝试返回完整列表时超时。所以我在操作中添加了一个大小为 200 的限制过滤器。

这导致的问题是,当我使用 CAML 查询外部列表时,它只搜索返回的 200 个条目,而不是完整列表。

我希望它搜索整个列表,但最多只返回 200 个匹配条目。

我怎样才能最好地做到这一点?

【问题讨论】:

  • 你有 BCS 作为 SQL 连接器还是 Assembly 连接器?
  • @MaksMatsveyeu 我对一般意义上的这个问题很感兴趣,即它应该与连接器无关。

标签: sharepoint caml bcs


【解决方案1】:

也许 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
    }
}

祝你好运

【讨论】:

  • 这是一个合理的解决方案,但不是最佳解决方案。消费者仍然需要对外部系统的实现有充分的了解才能了解如何查询它。理想情况下,外部列表可以简单地作为列表进行查询。
  • 没问题。是的,我确实这样做了。你知道我们吗?你住在瑞典吗?
  • 我从您的个人资料中看到您为沃尔沃工作,也许我们的道路会交叉.. :) 我知道哥德堡的沃尔沃在 SharePoint 上投入了大量资金。
  • 我周六刚从 GOT 回到美国 :) 我从一些同事那里听说过 Avega 这个名字。
  • 谢谢!我已经有一段时间没有使用 stackoverflow 了,只是看到了所有有助于回答我的问题的好建议,包括你的问题。
【解决方案2】:

根据您的查询结构和此处提供的信息,reports indicate &lt;RowLimit&gt; 实现了您想要的功能:

RowLimit 元素设置视图的行数限制。

语法

属性

  • 分页:可选布尔值。如果列表支持,则为 TRUE 逐页显示更多项目。如果 FALSE 或未指定,则 行限制是绝对的,没有查看更多项目的链接。

注意事项:注意the documentation中的备注。

您可能已经出于您的目的检查了这个(引用您的问题:“所以我在操作中添加了一个大小为 200 的限制过滤器。”)。那么,进入下一个问题:

这导致的问题是,当我使用 CAML 它只搜索返回的 200 个条目,而不是完整列表。

这看起来很奇怪。如果您确实在使用 &lt;RowLimit&gt; 并且文档是正确的:

RowLimit 元素设置视图的行数限制。

And:

&lt;RowLimit&gt; 标记位于视图(的直接子级)的架构定义中,因此不能嵌套在 &lt;Query&gt; 标记内。

那么你的嵌套查询应该在你的视图组件之前执行以满足你的限制语句的保证。作为推论,这应该允许您在查询定义的其余集合中执行结果分页。

在这些原则的基础上,我们可以构造一个这样的分页查询:

<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>

注意,如文档中所述,我们必须实现&lt;PagedRowset&gt;。如果不需要,我们在上面设置Paged='FALSE'

我可能完全不在这里,因为这似乎正是您已经尝试过的。但是,为了详尽地规划空间,建议它不会有坏处。

【讨论】:

  • 对于外部列表,很明显在“正常”的实现条件下,查询是不会传递到连接器进行翻译的。所以行为似乎是要求连接器提供尽可能多的项目,然后将过滤器查询和行限制条件应用于结果。
  • @RexM 同意。转到sharepoint.stackexchange.comthis answer 讨论了一些与 CAML 相关的 SOAP API 中 &lt;ViewFields&gt;/&lt;RowLimit&gt; 优先顺序的特性。当然,处理这个问题的最好方法是指定a query to be executed internally by Sharepoint instead of the web component,但我假设这样做的能力由于管理原因受到限制。如果没有更多信息,我不确定是什么原因造成的。
【解决方案3】:

很遗憾,这是查询外部列表的一个已知问题。但是,在 OOB Web 部件中,通过 XSLT 支持分页。 RowLimit 仅适用于 XSLT 而不适用于 CAML 查询。在外部列表中,没有服务器端分页,而是客户端分页,这意味着 SharePoint 会提取所有数据,然后在视图中设置筛选器限制。

【讨论】:

    【解决方案4】:

    这是无代码 BCS 本身并不能真正解决问题的场景之一。要么将其实现为数据库服务器上的存储过程,要么使用 Visual Studio 中内置的自定义 BDC 连接器。

    【讨论】:

    • 它也不适用于自定义 BCS 连接器。 “无代码”与此无关。仍然无法将查询执行推迟到连接器。我希望能有一些开箱即用的想法来寻找好的替代方案。
    【解决方案5】:

    如果您无法将整个 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();
    }
    

    这将为消费者打开大门,让他们也可以指定他们期望返回的对象类型。

    我们的查询数据接口实际上有更多的成员,它们为我们的查询提供程序添加了更多的扩展点,但我认为这个示例以简洁易懂的方式说明了这一点。

    只是想提出这个建议,因为它看起来与我们大约一年前遇到的情况几乎相同,而且这种策略对我们来说效果很好。

    【讨论】:

    • 我很高兴这对你很有效。在旧版 (SP 2007) 系统中,我们还有一个隐藏数据源的本土 LINQ 提供程序——它是 SharePoint 吗?是别的吗?消费者不需要知道。但我对这个问题提出了赏金,因为在我目前的场景中,它有完全不同的需求,我真的希望查询界面是 SharePoint 本身,而不是一些自定义代码。
    【解决方案6】:

    使用来自 SPQuerySPListItemCollection 的属性 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);
    }
    

    【讨论】:

    • 这不起作用。在第一次搜索之后(它只搜索限制过滤器指定的前 200 个文档),ListItemCollectionPosition 为空,因此它不会搜索接下来的 200 条记录...
    • 尝试添加 query.ViewAttributes = "Scope='RecursiveAll'";和 query.Folder = list.RootFolder;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-04
    相关资源
    最近更新 更多