本人微信公众号:微软动态CRM专家罗勇 ,回复278或者20180812可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me 。

我们知道Dynamics 365 Customer Engagement中当返回的结果集超过5000的时候就不会显示具体多少条,如果我想知道有多少条呢?有时候我还需要知道实体的记录数到底有多少?

怎么办?每次写个程序去查询具体有多少?普通用户不方便(可能需要安装比较新版本的.Net Framework),去数据库查询?麻烦且需要转换FetchXml的条件,去数据库查询还不能反映出要执行这个用户的权限能看到的记录数,所以可能得出错误的结论。

如果,有一个页面,你贴进去FetchXml,这个FetchXml可以来自高级查找下载的FetchXml,也有默认值,自己改下实体名和主键字段的名称,就可以帮我算出来到底有多少记录,那就好了。

如果你这么想,Follow me。

我的程序做法是先将这个最初的FetchXml(这个FetchXml最大可能来自高级查找中的下载 Fetch XML按钮,如果纯粹是计数的话,我已经写好模板了,不用去下载了)转换成执行聚合计算的FetchXml,通过Web API执行这个FetchXml,如果不报错,就是说没有超过5万行记录(AggregateQueryRecordLimit参数值默认五万),直接通过聚合计算出来,效率很好,比将记录获取过来计数快。

如果报错,则没有办法了,只有分页查询来计数了。将第一次获取到的记录集中的pagingcookie进行两次unescape,然后将其中的页码解析出来(我的程序是用正则表达式RegExp解析出来),

然后继续循环执行新的分页查询FetchXml,将前面解析出来的页码加1后作为这个分页查询FetchXml中的page属性值,原来的pagingCookie去掉istracking及其后面部分,

再将其中的 ", <, > 三个符号分别替换掉作为&quot;,&lt;,&gt; 后作为分页查询FetchXml的paging-cookie属性值,不断执行查询并计数,直到查询到没有更多结果为止。

下面是我建立的网页(HTML)类型的Web 资源 ly_/common/page/CountEntityRecords.htm 的源码:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>获取实体记录数_罗勇原创_www.luoyong.me</title>
  8. <style type="text/css">
  9. table {
  10. border-collapse: collapse;
  11. width: 100%;
  12. }
  13.  
  14. table, td, th {
  15. border: 1px solid black;
  16. }
  17.  
  18. tr:nth-of-type(odd) {
  19. background: #fff;
  20. }
  21.  
  22. tr:nth-of-type(even) {
  23. background: #F5FAFA;
  24. }
  25.  
  26. table tr:hover td {
  27. background-color: #ffff99;
  28. }
  29. </style>
  30. </head>
  31. <body>
  32. <textarea id="fetchxml" cols="120" rows="15">
  33. &lt;fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'&gt;
  34. &nbsp;&lt;entity name='contact'&gt;
  35. &nbsp;&nbsp;&lt;attribute name='contactid' /&gt;
  36. &nbsp;&nbsp;&lt;filter type='and'&gt;
  37. &nbsp;&nbsp;&nbsp;&lt;condition attribute='statecode' operator='eq' value='0' /&gt;
  38. &nbsp;&nbsp;&lt;/filter&gt;
  39. &nbsp;&lt;/entity&gt;
  40. &lt;/fetch&gt;
  41. </textarea>
  42. <br />
  43. &nbsp;&nbsp;&nbsp;&nbsp;<button id="convertit">查看记录数</button>
  44. <br />
  45. <hr />
  46. <textarea id="countResult" cols="120" rows="2" placeholder="这里显示超过AggregateQueryRecordLimit参数值(默认五万)的计数结果."></textarea>
  47. <br />
  48. <hr />
  49. <script src="../../../ClientGlobalContext.js.aspx" type="text/javascript"></script>
  50. <script type="text/javascript">
  51. var crmclientcontext =null;
  52. var clientUrl ="";
  53. var entitySetName="";
  54. var orgFetchXML = "";
  55. var pagingFetchXML = "";
  56. var aggFetchXML ="";
  57. var recordCount = 0;
  58.  
  59. function getPagingCookie(pagingCookie) {
  60. var pagingInfo = {};
  61. try {
  62. pagingCookie = unescape(unescape(pagingCookie));
  63. //console.log(pagingCookie);
  64. pagingCookie = pagingCookie.substring(pagingCookie.indexOf("pagingcookie") + 14, pagingCookie.indexOf("istracking") - 1);
  65. pagingInfo.nextPageNum = parseInt(pagingCookie.match(/\d+/)[0]) + 1;
  66. pagingCookie = pagingCookie.replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  67. pagingCookie = pagingCookie.substring(0, pagingCookie.lastIndexOf('%'));
  68. pagingInfo.pagingCookie = pagingCookie;
  69. } catch (e) {
  70. throw new Error(e);
  71. }
  72. return pagingInfo;
  73. }
  74.  
  75. function executeFetchXML(entitySetName, fetchXML, successCallback, errorCallback) {
  76. var req = new XMLHttpRequest();
  77. req.open("GET", clientUrl + entitySetName + "?fetchXml=" + encodeURI(fetchXML), false);
  78. req.setRequestHeader("Accept", "application/json");
  79. req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  80. req.setRequestHeader("OData-MaxVersion", "4.0");
  81. req.setRequestHeader("OData-Version", "4.0");
  82. req.onreadystatechange = function () {
  83. if (this.readyState == 4 /* complete */) {
  84. req.onreadystatechange = null;
  85. if (this.status == 200) {
  86. successCallback(JSON.parse(this.responseText));
  87. }
  88. else {
  89. errorCallback(JSON.parse(this.responseText));
  90. }
  91. }
  92. }
  93. req.send();
  94. }
  95.  
  96. function errorCallback(errorJson) {
  97. console.log(errorJson);
  98. if (typeof errorJson.error.message === "string") {
  99. Xrm.Utility.alertDialog("执行FetchXml出错!" + errorJson.error.message);
  100. }
  101. else {
  102. Xrm.Utility.alertDialog("执行FetchXml出错,请查看console中的输出信息!");
  103. }
  104. }
  105.  
  106. //当执行聚合FetchXml失败,原因是因为超过AggregateQueryRecordLimit参数值的时候执行的函数
  107. function errorCallbackHandleAggregateQueryRecordLimit(errorJson) {
  108. console.log(errorJson);
  109. if (typeof errorJson.error.message === "string") {
  110. if (errorJson.error.message.indexOf('AggregateQueryRecordLimit') >= 0) {
  111. console.log("记录数超过了AggregateQueryRecordLimit参数的值(默认五万),将执行分页查询来计数.");
  112. executeFetchXML(entitySetName, orgFetchXML, successCallback, errorCallback);
  113. }
  114. else{
  115. Xrm.Utility.alertDialog("执行FetchXml出错!" + errorJson.error.message);
  116. }
  117. }
  118. else {
  119. Xrm.Utility.alertDialog("执行FetchXml出错,请查看console中的输出信息!");
  120. }
  121. }
  122.  
  123. //分页查询调用成功的执行函数
  124. function successCallback(responseJson) {
  125. if (responseJson.value.length >= 1) {
  126. recordCount += responseJson.value.length;
  127. console.log(recordCount + " : " + new Date());
  128. }
  129. if (responseJson.hasOwnProperty("@Microsoft.Dynamics.CRM.fetchxmlpagingcookie")) {
  130. var pageInfo = getPagingCookie(responseJson["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"]);
  131. pagingFetchXML = orgFetchXML.replace("<fetch", "<fetch page='" + pageInfo.nextPageNum + "' paging-cookie='" + pageInfo.pagingCookie + "'");
  132. //console.log(pagingFetchXML);
  133. executeFetchXML(entitySetName, pagingFetchXML, successCallback, errorCallback);
  134. }
  135. else {
  136. console.log("分页查询完毕啦!" + " : " + new Date());
  137. document.getElementById('countResult').innerText = "总记录条数是" + recordCount;
  138. var tables = document.getElementsByTagName('table');
  139. for (var i = 0; i < tables.length; i++) {
  140. tables[i].parentNode.removeChild(tables[i]);
  141. }
  142. }
  143. }
  144.  
  145. //一个聚合函数可以查询出记录结果的时候显示方式
  146. function successCallbackToShowData(responseJson) {
  147. document.getElementById('countResult').innerText = "罗勇(www.luoyong.me)原创的程序执行完毕,结果见下表:";
  148. var tables = document.getElementsByTagName('table');
  149. for (var i = 0; i < tables.length; i++) {
  150. tables[i].parentNode.removeChild(tables[i]);
  151. }
  152. if (responseJson.value.length >= 1) {
  153. var recordCount = responseJson.value.length;
  154. var cols = [];
  155. var body = document.getElementsByTagName('body')[0];
  156. var tbl = document.createElement('table');
  157. var tbdy = document.createElement('tbody');
  158. for (var i = 0; i < recordCount; i++) {
  159. for (var propkey in responseJson.value[i]) {
  160. if (propkey != "@odata.etag" && cols.indexOf(propkey) <= -1) {
  161. cols.push(propkey);
  162. }
  163. }
  164. }
  165. cols.sort();
  166. var tr = document.createElement("tr");
  167. var td = document.createElement('td');
  168. td.appendChild(document.createTextNode("序号"));
  169. tr.appendChild(td);
  170. cols.forEach(function (ele) {
  171. td = document.createElement('td');
  172. td.appendChild(document.createTextNode(ele));
  173. tr.appendChild(td);
  174. });
  175. tbdy.appendChild(tr);
  176. for (var i = 0; i < recordCount; i++) {
  177. var tr = document.createElement('tr');
  178. var td = document.createElement('td');
  179. td.appendChild(document.createTextNode((i + 1).toString()));
  180. tr.appendChild(td);
  181. cols.forEach(function (ele) {
  182. if (responseJson.value[i].hasOwnProperty(ele)) {
  183. td = document.createElement('td');
  184. td.appendChild(document.createTextNode(responseJson.value[i][ele]));
  185. tr.appendChild(td);
  186. }
  187. else {
  188. td = document.createElement('td');
  189. tr.appendChild(td);
  190. }
  191. });
  192. tbdy.appendChild(tr);
  193. }
  194. tbl.appendChild(tbdy);
  195. body.appendChild(tbl);
  196. }
  197. }
  198.  
  199. document.getElementById('convertit').onclick = function () {
  200. document.getElementById('countResult').innerText = "罗勇(www.luoyong.me)原创的程序开始执行计数啦,请稍等...";
  201. var tables = document.getElementsByTagName('table');
  202. for (var i = 0; i < tables.length; i++) {
  203. tables[i].parentNode.removeChild(tables[i]);
  204. }
  205. if (typeof GetGlobalContext != "undefined") {
  206. crmclientcontext = GetGlobalContext();
  207. } else {
  208. throw new Error("引入ClientGlobalContext.js.aspx失败,请检查!");
  209. }
  210. clientUrl = crmclientcontext.getClientUrl() + "/api/data/v8.2/";
  211. orgFetchXML = document.getElementById("fetchxml").value.replace(/"/gm, "'");
  212. var entityLogicalName = orgFetchXML.match(/<entity\s?name\s?=\s?\'\w+\'/ig)[0].match(/\'.+\'/i)[0].replace(/\'/g, "");
  213. var req = new XMLHttpRequest();
  214. req.open("GET", clientUrl + "EntityDefinitions?$select=EntitySetName&$filter=LogicalName" + encodeURI(" eq '" + entityLogicalName + "'"), false);
  215. req.setRequestHeader("Accept", "application/json");
  216. req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  217. req.setRequestHeader("OData-MaxVersion", "4.0");
  218. req.setRequestHeader("OData-Version", "4.0");
  219. req.onreadystatechange = function () {
  220. if (this.readyState == 4 /* complete */) {
  221. req.onreadystatechange = null;
  222. if (this.status == 200) {
  223. entitySetName = JSON.parse(this.responseText).value[0].EntitySetName;
  224. }
  225. else {
  226. errorCallback(JSON.parse(this.responseText));
  227. }
  228. }
  229. };
  230. req.send();
  231. aggFetchXML = orgFetchXML.replace("<fetch", "<fetch aggregate='true'");
  232. var attrAt = aggFetchXML.search(/<attribute/g);
  233. aggFetchXML = aggFetchXML.substring(0, attrAt + 10) + " aggregate='count' alias='recordscount'" + aggFetchXML.substring(attrAt + 10);
  234. console.log("根据最初FetchXml改造后的聚合FetchXml =" + aggFetchXML);
  235. executeFetchXML(entitySetName, aggFetchXML, successCallbackToShowData, errorCallbackHandleAggregateQueryRecordLimit);
  236. };
  237. </script>
  238. </body>
  239. </html>

下面我们来看看效果,首先看不足5万行记录结果如下,查询的是某个实体的状态为有效(statecode=0)的记录数,当然你要查看所有记录数,将filter元素的内容手动删除再执行查询即可。

再来看一个超过5万行记录的结果,计数结果为66152行,正确。从日志中可以看到,首先执行聚合查询的时候报错了,然后就分页查询,一次查询出最多5000行(一次返回5000行记录大概耗时10秒,所以记录数很多的时候请耐心点),计算下总数。

当然,有个注意的地方,查询是以当前用户身份去做的,结果集也是当前用户能看到的结果集,因为权限控制,部分记录当前用户可能看不到,那么和真正的所有的符合条件的所有记录数(当前用户看到的,看不到的所有的)可能有差别,因为这个结果集只包括了当前用户有读取权限的结果集,那些没有读取权限的记录不会被统计进去。

当然,你如果用系统管理员角色用户来执行的,统计的就是真正的所有的符合条件的记录集。

无依赖简单易用的Dynamics 365实体记录数计数器并能计算出FetchXml返回的记录数的更多相关文章

  1. 无依赖简单易用的Dynamics 365公共视图克隆工具

    本人微信公众号:微软动态CRM专家罗勇 ,回复279或者20180818可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . Dy ...

  2. 自定义控制台程序导出Dynamics 365实体信息到Excel中。

    本人微信公众号:微软动态CRM专家罗勇 ,回复281或者20181116可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 有时 ...

  3. Dynamics 365 POA表记录的查询

    微软动态CRM专家罗勇 ,回复313或者20190311可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . PrincipalO ...

  4. 介绍Dynamics 365 Customer Engagement中的备用键(alternate key)

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  5. Dynamics 365 POA表记录的产生

    微软动态CRM专家罗勇 ,回复314或者20190311可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 前面的博文 Dyna ...

  6. 为Dynamics 365写一个简单程序实现解决方案一键迁移

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复258或者20170627可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  7. 工作中经常用到github上优秀、实用、轻量级、无依赖的插件和库

    原文收录在我的 GitHub博客 (https://github.com/jawil/blog) ,喜欢的可以关注最新动态,大家一起多交流学习,共同进步,以学习者的身份写博客,记录点滴. 按照格式推荐 ...

  8. 工作中经常用到 github 上优秀、实用、轻量级、无依赖的插件和库

    原文收录在 GitHub博客 ( https://github.com/jawil/blog ) ,喜欢的可以关注最新动态,大家一起多交流学习,共同进步,以学习者的身份写博客,记录点滴. 由于gith ...

  9. DataAccess通用数据库访问类,简单易用,功能强悍

    以下是我编写的DataAccess通用数据库访问类,简单易用,支持:内联式创建多个参数.支持多事务提交.支持参数复用.支持更换数据库类型,希望能帮到大家,若需支持查出来后转换成实体,可以自行扩展dat ...

随机推荐

  1. SQL Server Cast、Convert数据类型转换

    一.概述 本篇文章转载来着官网在线文档,文章主要介绍SQL Server数据类型转换相关语法.隐式转换.Date样式等. 语法 Syntax for CAST: CAST ( expression A ...

  2. [Swift]LeetCode216. 组合总和 III | Combination Sum III

    Find all possible combinations of k numbers that add up to a number n, given that only numbers from ...

  3. [Swift]LeetCode398. 随机数索引 | Random Pick Index

    Given an array of integers with possible duplicates, randomly output the index of a given target num ...

  4. [SQL]LeetCode577.员工奖金 | Employee Bonus

    Select all employee's name and bonus whose bonus is < 1000. Table:Employee +-------+--------+---- ...

  5. [C#]批量修改文件后缀名

    代码: /// <summary> /// 修改文件后缀 /// </summary> /// <param name="extension"> ...

  6. vue全家桶项目搭建(vue-cli 2.9.6+vue-router+vuex+axios)

    一.安装vue-cli + vue-router + vuex + axios 1.安装vue-cli 2.创建项目 3.安装vuex和axios 二.搭建项目目录结构,如下所示: 1.assets目 ...

  7. 【Sqoop篇】----Sqoop从搭建到应用案例

    一.前述 今天开始讲解Sqoo的用法搭建和使用.Sqoop其实功能非常简单.主要用于在Hadoop(Hive)与传统的数据库(mysql.postgresql...)间进行数据的传递,可以将一个关系型 ...

  8. BBS论坛(十七)

    17.首页导航条实现和代码抽离 (1)temlates/common/_head.html <meta name="csrf-token" content="{{ ...

  9. H5本地存储sessionStorage和localStorage的区别

    sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁.因此sessionStorage不是一种持久化的本地 ...

  10. XSS Stored 测试

    dvwa存储型XSS 存储型XSS:会把用户输入的数据“存储”在服务器端,一般出现在需要用户可以输入数据的地方,比如网站的留言板.评论等地方,当网站这些地方过滤不严格的时候,就会被黑客注入恶意攻击代码 ...