【转】4.2SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 2)

(三)使用SPQuery进行列表查询

1、概述

列表查询主要是指在一个指定的列表(或文档库)中按照某些筛选、排序条件进行查询。列表查询主要使用SPQuery对象,以及SPList的GetItems方法,将SPQuery作为参数传递,返回查询到的列表条目集合,即SPListItemCollection类型。

在使用SPQuery进行列表查找的时候,其中一些属性指定了其查找的特性:

Query属性:通过该属性指定CAML格式的筛选条件和排序条件(见上文),如果不指定,则默认返回范围内的所有条目;

Folder属性:通过该属性指定在特定文件夹范围内进行查找,如果不指定,则为列表或文档库的根文件夹;

RowLimit属性:指定一次返回多少个条目,如果不指定,则返回所有条目;通过这个属性与ListItemCollectionPosition属性配合,可以实现列表条目的分页查找;出于性能考虑,建议在一般情况下,都指定一个合理的RowLimit值,在SharePoint 2010中,系统也会自动对查询返回结果的数量上进行一些限制和过滤(见后文关于查询阈值的描述);

ViewFields属性:指定CAML格式的返回字段(见上文),如果不指定,则默认返回所有字段;

ViewAttributes属性:这个属性主要和Folder属性配合使用,用于指定查询范围,如果不指定,则查找指定文件夹下的条目和子文件夹(详见后文)。

下面是一个列表查询的例子:

   1: using(SPSite site = new SPSite("http://sp2010/book"))
   2: {
   3:   using(SPWeb web = site.OpenWeb())
   4:   {
   5:     SPList list = web.Lists["Chapters"];
   6:     SPQuery query = new SPQuery();
   7:     query.Query = "<Where><Contains><FieldRef Name='Title'/>" +
   8:         "<Value Type='Text'>sp</Value></Contains></Where>" +
   9:         "<OrderBy><FieldRef Name='Created'/></OrderBy>";
  10:     query.ViewFields = "<FieldRef Name='Title'/>";
  11:     SPListItemCollection items = list.GetItems(query);
  12:     foreach(SPListItem itm in items)
  13:       Console.WriteLine(Convert.ToString(itm["Title"]));
  14:   }
  15: }

这是一个比较简单的列表查询,查询了“Chapters”这个列表的根目录中,“标题”字段包含字符串“sp”(SharePoint查询中是否区分大小写,由内容数据库的相关规则指定,默认规则安装的数据库不区分字母的大小写)的那些列表条目(可能还有文件夹),并输出它们的标题。

 

2、查询范围(Scope)

通过配合使用SPQuery的Folder属性和ViewAttributes属性,可以控制查询时候的范围。

首先需要说明的是,在进行查询的时候,普通列表条目(或者文档库中的文件)与文件夹是划分查询范围的两个重要条件。在查询的时候,共有4种查询范围的设定:

  • 不指定ViewAttributes,则查询的是Folder对应的文件夹下面的列表条目(或文档)以及子文件夹,就上面的例子来说,查询的就是列表的根目录,会返回根目录中标题包含“sp”的列表条目,以及名称包含“sp”的子文件夹。
  • ViewAttributes = "Scope='FilesOnly'"。顾名思义,当指定了这个范围之后,查询的就是Folder对应文件夹下面的列表条目(或文档)。如果把这个属性加到上面的例子中,返回的将不再包括列表根目录下的子文件夹——即使它们的名称包含“sp”。
  • ViewAttributes = "Scope='Recursive'"。当指定这个查询范围的时候,它查询的范围是Folder指定的文件夹及其子文件夹下的所有列表条目(或文档),不管它们在哪个子文件夹下。
  • ViewAttributes = "Scope='RecursiveAll'"。同样的,它查询的范围是Folder指定的文件夹及其子文件夹下中的所有列表条目(或文档)以及文件夹。

注意:当查询结果同时包含了文件夹和普通条目(或文档)的时候,不论排序条件是如何指定的,在返回的结果中必然是文件夹在前、普通条目(或文档)在后。

 

3、分页查询

如果列表中一次查询返回的结果非常非常多,势必会造成一定的性能损失,通过RowLimit属性可以在一定程度上控制性能的降低,但是仅通过RowLimit属性,就只能返回查询结果中的前若干条结果,如果用户想要再查看后面的结果,就要配合使用到SPQuery的ListItemCollectionPosition属性进行分页查找。

在查找返回结果的SPListItemCollection对象中,有一个ListItemCollectionPosition属性,包含了当前查询结果的分页信息。将这个属性传递到下一次查找过程中使用的SPQuery的对应属性中,就可以直接返回下一页的查找结果,直到返回结果中的该属性为null,就表示已经查找到了最后一页。每页包含多少个结果由RowLimit属性来指定。

下面的程序就是在之前程序的基础上,增加了分页查找的例子:

   1: using(SPSite site = new SPSite("http://sp2010/book"))
   2: {
   3:   using(SPWeb web = site.OpenWeb())
   4:   {
   5:     SPList list = web.Lists["Chapters"];
   6:     SPQuery query = new SPQuery();
   7:     query.Query = "<Where><Contains><FieldRef Name='Title'/>" +
   8:         "<Value Type='Text'>sp</Value></Contains></Where>" +
   9:         "<OrderBy><FieldRef Name='Created'/></OrderBy>";
  10:     query.ViewFields = "<FieldRef Name='Title'/>";
  11:     query.RowLimit = 5;
  12:     SPListItemCollection items = null;
  13:     int currentPage = 1;
  14:     do
  15:     {
  16: items = list.GetItems(query);
  17: Console.WriteLine(string.Format("Page {0}:", currentPage));
  18:     foreach(SPListItem itm in items)
  19:           Console.WriteLine(Convert.ToString(itm["Title"]));
  20:         query.ListItemCollectionPostion = 
  21:                  items.ListItemCollectionPosition;
  22:       currentPage++;
  23:     }while(items.ListItemCollectionPosition != null);
  24:   }
  25: }

细心的读者可能会注意到SharePoint内置的列表视图在进行分页的时候,只有上一页和下一页的链接按钮,而没有办法直接跳转到第x页,在翻到最后一页之前,也没有办法知道在这个视图下一共有多少个条目。在经过上面的介绍之后,现在您应当已经能想到这个现象背后的原因了。SharePoint内置提供了如上面程序那样向后翻页的机制,读者可以在前后翻页的时候注意一下Url地址的变化,自行思考如何来实现“上一页”的效果。实际上,在SharePoint 2003的时代,列表视图里面就只有“下一页”的按钮,而没有“上一页”的按钮。

 

4、通过查阅项进行列表关联查询

在进行列表查询的时候,使用之前介绍的CAML语法,可以很容易地在一个列表中根据其中字段的不同筛选条件进行查询。但是从应用的角度来讲,仅将查询局限在一个表中是远远不够的,我们在开发传统应用的时候必然会遇到多个表之间进行关联查询的场景。这里我们就将介绍,在SharePoint中,如何实现这种多表之间关联的查询。

为了能够更加清楚的说明问题,在这里我们不妨假定一个经过简化后的实际场景:一个软件销售公司拥有自己的销售人员列表,其中包含了销售人员的姓名、擅长的销售领域等等;同时也拥有一张订单列表,包含了所销售的产品的名称、数量、单价,以及这张订单的销售人员的姓名。于是我们不难设计出如下的数据表结构:

image

在SharePoint中实现这样的数据结构,我们可以想到创建“Sales”和“Orders”这两个列表,并在“Orders”列表中创建一个“Saler”查阅项来查阅“Sales”列表的姓名字段,如下图:

image

 

 

此时,如果我们需要查找“张三”这个销售卖出去的所有订单,可以使用如下的CAML:

   1: <Where><Eq>
   2:   <FieldRef Name='Saler' />  <!--注意这里使用了字段内部名称 -->
   3:   <Value Type='Lookup'>张三</Value>
   4: </Eq></Where>

或者更准确的(考虑到可能有多个名叫“张三”的销售):

   1: <Where><Eq>
   2:   <FieldRef Name='Saler' LookupId='TRUE' />
   3:   <Value Type='Lookup'>1</Value> <!--1是我们关心的那个“张三”的ID-->
   4: </Eq></Where>

于是,很自然地我们通过查阅项实现了最简单的两个列表关联查询。

直到有一天,老板希望能够查看一下那些专长领域是Office的销售都卖出过哪些订单。按照一般的思路,我们可以先在销售列表中找到那些专长领域是Office的销售有哪些,然后再分多次去订单列表中查询,最后把查询结果拼在一起。但是有没有更简单高效的方法呢?在SharePoint 2010中,为此提供了两种方式:

方法一:使用SharePoint 2010查阅项新增的映射栏(Project Fields)的功能,将姓名、领域两个字段同时映射到销售列表中,见下图:

image

 

 

于是,我们就可以使用映射到销售列表中的“Sales:领域”这一字段进行查询了:

   1: <Where><Eq>
   2:   <FieldRef Name='SalerField' />  <!--依然是字段的内部名称 -->
   3:   <Value Type='Lookup'>Office</Value>
   4: </Eq></Where>

方法二:使用SharePoint 2010增加的SPQuery的列表关联查询功能。

这里,我们需要先借助SPQuery的Joins属性声明两个列表的关联,再使用ProjectedFields属性创建一个虚拟的映射字段,在这个虚拟字段上使用CAML进行查询。SPQuery的创建过程如下:

   1: SPQuery query = new SPQuery();
   2:  
   3: query.Joins = @"<Join Type='LEFT' ListAlias='Sales'>
   4:                     <Eq>
   5:                         <FieldRef Name='Saler' RefType='ID'/>
   6:                         <FieldRef List='Sales' Name='ID'/>
   7:                     </Eq>
   8:                 </Join>";
   9:  
  10: query.ProjectedFields = @"<Field Name='SalerField' Type='Lookup' 
  11: List='Sales' ShowField='Field'/>";
  12:  
  13: query.Query = @"<Where><Eq>
  14:                     <FieldRef Name='SalerField'/>
  15:                     <Value Type='Lookup'>Office</Value>
  16:                 </Eq></Where>";

在这段程序中,首先我们创建了一个列表关联(Join),其类型是左外连接(Type=LEFT),并将这个关联赋予一个列表别名(ListAlias='Sales'),关联是建立在当前列表(即订单列表)的“Saler”字段(查阅项,本质是查阅ID)与Sales列表(这个名称必须与Join中声明的列表别名相同)的ID字段。实际上,这一段CAML如果“翻译”成SQL语句的话(这里只是为了能够让传统的开发人员更容易理解这段CAML,并不表示SharePoint实际存储的数据表结构。),看上去就变得熟悉多了:

   1: Orders LEFT JOIN Sales
   2: ON
   3: Orders.Saler = Sales.ID

接下来,为了能够在查询条件中使用“领域”这一字段,需要把销售表中的“领域”映射为订单表中的一个虚拟字段(ProjectedField),并给这个字段随意指定一个名称(比如程序中的“SalerField”。该字段来自Sales列表(List='Sales',同样要求与Join中定义的的列表别名相同)的“Field”字段(ShowField='Field',“领域”的内部名称),类型为查阅项(Type='Lookup')。

之后,我们就可以把这个映射字段“SalerField”当作订单列表中的字段在查询使用了。

这两种方法所达到的效果是完全一样的,区别只是在于创建一个真正的映射字段,还是创建一个虚拟的映射字段。

由于我们的程序工作良好,为公司提高了效率,公司不断壮大发展,在其它城市也成立了分部。此时,为了能够管理其他城市的销售人员,我们的数据结构就要发生变动了,需要引入一张新的“城市”列表,并且在销售列表上为每个销售附加上城市的信息:

image

于是,在SharePoint中,我们也相应的创建了一个城市列表,并且在销售列表中创建了一个引用了城市列表的查阅项:

image

这个时候,老板必定会希望能够查看一下某个特定城市的所有订单的销售情况。当然,我们仍然可以先找到这个城市的所有销售,然后分别查找出他们的订单并把结果合并起来。不过在SharePoint 2010中,仍然有更直接的方法,那就是使用列表的关联查询。

遗憾的是,SharePoint的查阅项在映射多个列的时候,无法再从中选择一个查阅项,因此我们只能通过方法二中的技术来实现这一需求。这个SPQuery的创建程序片段如下:

   1: SPQuery query = new SPQuery();
   2:  
   3: query.Joins = @"<Join Type='LEFT' ListAlias='Sales'>
   4:                     <Eq>
   5:                         <FieldRef Name='Saler' RefType='Id' />
   6:                         <FieldRef List='Sales' Name='ID' />
   7:                     </Eq>
   8:                 </Join>
   9:                 <Join Type='LEFT' ListAlias='Cities'>
  10:                     <Eq>
  11:                         <FieldRef List='Sales' Name='City' RefType='Id' />
  12:                         <FieldRef List='Cities' Name='ID' />
  13:                     </Eq>
  14:                 </Join>";
  15:  
  16: query.ProjectedFields = @"<Field Name='SaleCity' Type='Lookup' 
  17: List='Cities' ShowField='Title'/>";
  18:  
  19: query.Query = @"<Where>
  20:                     <Eq>
  21:                         <FieldRef Name='SaleCity' />
  22:                         <Value Type='Text'>北京</Value>
  23:                     </Eq>
  24:                 </Where>";

 

 

 

与之前的程序相比,这段程序只是多做了一次Join,将关联的列表从两个变成了三个,它“相当于”如下的SQL语句(同样,这里只是为了方便传统的开发人员能够理解SharePoint中的Join的写法):

   1: Orders LEFT JOIN Sales
   2: ON
   3: Orders.Saler = Sales.ID
   4: LEFT JOIN Cities
   5: ON
   6: Sales.City = City.ID

程序的其他部分就不再赘述了。

我们可以看到,在SharePoint 2010中,列表查阅项的功能已经有了极大的增强,这代表着今天实现列表与列表之间的关联越来越方便,可以更便捷地搭建出我们所需的应用。不过也应当注意到,随着关联的日趋复杂,CAML的编写也跟着越来越复杂,即使是很有经验的程序员也难免在这个地方出现一些手误,而这种错误在程序调试过程中又是很难发现的。好在SharePoint 2010为我们提供了一项新的查询技术,我们将在下一节中对其进行详细介绍。

 

5、列表查询的注意事项

在进行列表查询的时候,有一点是特别需要注意的,那就是在进行不同列表的不同查询条件的查询情况下,必须每次重新构造SPQuery对象。换句话说,每做一次查询(只要查询条件不同),就要重新new一个SPQuery对象出来。否则,在程序运行的时候,仍会以最初的条件进行查询。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【转】理解SQL Server的安全对象和权限

理解安全对象(Securable) 安全对象&#xff0c;是SQL Server 数据库引擎授权系统控制对其进行访问的资源。通俗点说&#xff0c;就是在SQL Server权限体系下控制的对象&#xff0c;因为所有的对象(从服务器&#xff0c;到表&#xff0c;到视图触发器等)都在SQL Server的权限体系…

【NC14 按之字形顺序打印二叉树】

描述 给定一个二叉树&#xff0c;返回该二叉树的之字形层序遍历&#xff0c;&#xff08;第一层从左向右&#xff0c;下一层从右向左&#xff0c;一直这样交替&#xff09; 数据范围&#xff1a;0 \le n \le 15000≤n≤1500,树上每个节点的val满足 |val| < 100∣val∣<1…

SharePoint 2010文档库批量下载文档的实现

在SharePoint 2010文档库中&#xff0c;结合单选框&#xff0c;在Ribbon中提供了批量处理文档的功能&#xff0c;比如&#xff0c;批量删除、批量签出、批量签入等&#xff0c;但是&#xff0c;很遗憾&#xff0c;没有提供批量下载&#xff0c;如图: 若选中多个文档后&#xff…

【LeetCode - 42. 接雨水】

42. 接雨水 难度困难3164 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 […

【LeetCode】第283场周赛题解

本场题题目不难&#xff0c;但是力求写出精简优雅的代码&#xff0c;还是有需要学习的地方的。 第一题 力扣 class Solution:def cellsInRange(self, s: str) -> List[str]:ans []a,b,c,d s[0],s[1],s[3],s[4]for i in range(ord(a), ord(c)1):for j in range(int(b),int…

Linq to Sql : 三种事务处理方式

Linq to SQL支持三种事务处理模型&#xff1a;显式本地事务、显式可分发事务、隐式事务。(from MSDN: 事务 (LINQ to SQL))。MSDN中描述得相对比较粗狂&#xff0c;下面就结合实例来对此进行阐述。 0. 测试环境 OSWindows Server 2008 Enterprise sp1IDEVisual Studio 2008, …

【LeetCode - 33】搜索旋转排序数组(二分)

力扣 解题报告&#xff1a; 二分。但是有不少细节要考虑清楚。 所以干脆考虑另一种二分的方式。也就是第二次二分的时候&#xff0c;把两半数组给拼成一个完整的数组&#xff0c;当然下标需要是虚拟的&#xff0c;这一步可以用偏移量取模完成。这样就不需要考虑边界情况了。 …

【LeetCode - 1765】. 地图中的最高点

力扣 解题报告&#xff1a; 多元BFS。 进阶一下&#xff1a; 二维数组&#xff0c;1表示等高线&#xff0c;0表示平地&#xff0c;比如 输入 010 111 010 输出 010 121 010输入 010 101 010 输出 010 111 010即输入一个二维地图&#xff0c;保证等高线一定是闭合的环&#x…

【转】微服务架构下分布式事务方案

1 微服务的发展 微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务&#xff0c;这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇&#xff0c;很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。Hailo有160个不同服务构成…

【LeetCode - 443】压缩字符串(模拟)

解题报告&#xff1a; 直接模拟。 class Solution { public:int compress(vector<char>& chars) {int p 0;for(int i 0; i<chars.size();) {int j i1;while(j<chars.size() && chars[j] chars[i]) j;chars[p] chars[i];if(j-i > 1) {int cnt…

Linq to SQL之使用事务

事务是一个原子的工作单位&#xff0c;必须完整的完成单位里的所有工作&#xff0c;要么全部执行&#xff0c;要么全部都不执行。如果提交事务&#xff0c;则事务执行成功&#xff1b;如果回滚事务&#xff0c;则事务执行失败。 事务具备4个基本特性--ACID(原子性、一致性、孤立…

【LeetCode - 798】得分最高的最小轮调(转化法)

解题报告&#xff1a; 思路一&#xff1a;这题首先说一个nlogn的方法。 首先一个主客转化&#xff0c;题目描述是说把数组做翻转&#xff0c;idx不变&#xff0c;然后nums[i]和i作比较。那么我们可以转化为让数组不变&#xff0c;idx转变&#xff0c;即&#xff1a;假设刚开始…

【转】聊聊分布式事务,再说说解决方案

前言 最近很久没有写博客了&#xff0c;一方面是因为公司事情最近比较忙&#xff0c;另外一方面是因为在进行 CAP 的下一阶段的开发工作&#xff0c;不过目前已经告一段落了。 接下来还是开始我们今天的话题&#xff0c;说说分布式事务&#xff0c;或者说是我眼中的分布式事务…

【LeetCode - 2049】统计最高分的节点数目

解题报告&#xff1b; 直接dp。注意mx也得longlong AC代码&#xff1a; class Solution { public:vector<int> vv[200005];int sum[200005];long long ans[200005];int n;void dfs(int x) {ans[x] 1; sum[x] 1;for(int i 0; i<vv[x].size(); i) {dfs(vv[x][i]);s…

【LeetCode每日一题】2024. 考试的最大困扰度

​​​​​​力扣 解题报告&#xff1a; 因为只有T和F两个元素&#xff0c;不难证明单向性。尺取法解决。当然这题也可以二分。 AC代码&#xff1a; class Solution { public:int maxConsecutiveAnswers(string answerKey, int k) {int l 0, r 0;int T 0, F 0;int ans …

2022-08-20-网易笔试题

写在前面 题目收集来源自网络&#xff0c;前四题是开发岗的&#xff0c;后四题是算法岗的&#xff0c;因为代码无处提交&#xff0c;不一定正确&#xff0c;就不贴出来了&#xff0c;这里只写一下我的思路吧~欢迎大家一起讨论~~ 1、 思路&#xff1a;因为最大1e9&#xff0c…

TUN/TAP设备浅析(一) -- 原理浅析

TUN/TAP设备浅析 TUN设备 TUN 设备是一种虚拟网络设备&#xff0c;通过此设备&#xff0c;程序可以方便地模拟网络行为。TUN 模拟的是一个三层设备,也就是说,通过它可以处理来自网络层的数据&#xff0c;更通俗一点的说&#xff0c;通过它&#xff0c;我们可以处理 IP 数据包…

2022-08-21 星环科技-C++开发笔试

1、 思路&#xff1a;拓扑排序&#xff0c;不解释了 2、 思路&#xff1a; 本来以为他是一个图论问题&#xff0c;找最大环。 但其实对于这种情况&#xff0c;他是要输出0的&#xff0c;而不是9&#xff0c;所以他不是一个图论问题&#xff0c;他带有顺序性&#xff0c;这种可…

【算法练习题】位运算(贪心)

题目&#xff1a; 解题报告&#xff1a; 首先预备几个结论&#xff1a; 1、对于两个数a,b &#xff0c;生成a&b和a|b&#xff0c;则一定对应一个常数c&#xff0c;使得生成的两个数分别是ac和a-c。 2、对于两个数a,b&#xff0c;生成a&b和a|b的前后&#xff0c;二进…

TUN/TAP设备浅析(三) -- TUN/TAP设备的应用

上一篇文章主要讲述了TUN/TAP设备的一些原理&#xff0c;你可能会好奇&#xff0c;TUN/TAP设备究竟有什么用处呢&#xff1f;所以这篇文章&#xff0c;我想用一些实际的例子来回答这个问题。 例子源自陈硕老师的博客&#xff0c;博文中关于TUN/TAP设备的使用非常典型&#xff…