无依赖简单易用的Dynamics 365实体记录数计数器并能计算出FetchXml返回的记录数
本人微信公众号:微软动态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及其后面部分,
再将其中的 ", <, > 三个符号分别替换掉作为",<,> 后作为分页查询FetchXml的paging-cookie属性值,不断执行查询并计数,直到查询到没有更多结果为止。

下面是我建立的网页(HTML)类型的Web 资源 ly_/common/page/CountEntityRecords.htm 的源码:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>获取实体记录数_罗勇原创_www.luoyong.me</title>
<style type="text/css">
table {
border-collapse: collapse;
width: 100%;
} table, td, th {
border: 1px solid black;
} tr:nth-of-type(odd) {
background: #fff;
} tr:nth-of-type(even) {
background: #F5FAFA;
} table tr:hover td {
background-color: #ffff99;
}
</style>
</head>
<body>
<textarea id="fetchxml" cols="120" rows="15">
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='contact'>
<attribute name='contactid' />
<filter type='and'>
<condition attribute='statecode' operator='eq' value='0' />
</filter>
</entity>
</fetch>
</textarea>
<br />
<button id="convertit">查看记录数</button>
<br />
<hr />
<textarea id="countResult" cols="120" rows="2" placeholder="这里显示超过AggregateQueryRecordLimit参数值(默认五万)的计数结果."></textarea>
<br />
<hr />
<script src="../../../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script type="text/javascript">
var crmclientcontext =null;
var clientUrl ="";
var entitySetName="";
var orgFetchXML = "";
var pagingFetchXML = "";
var aggFetchXML ="";
var recordCount = 0; function getPagingCookie(pagingCookie) {
var pagingInfo = {};
try {
pagingCookie = unescape(unescape(pagingCookie));
//console.log(pagingCookie);
pagingCookie = pagingCookie.substring(pagingCookie.indexOf("pagingcookie") + 14, pagingCookie.indexOf("istracking") - 1);
pagingInfo.nextPageNum = parseInt(pagingCookie.match(/\d+/)[0]) + 1;
pagingCookie = pagingCookie.replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
pagingCookie = pagingCookie.substring(0, pagingCookie.lastIndexOf('%'));
pagingInfo.pagingCookie = pagingCookie;
} catch (e) {
throw new Error(e);
}
return pagingInfo;
} function executeFetchXML(entitySetName, fetchXML, successCallback, errorCallback) {
var req = new XMLHttpRequest();
req.open("GET", clientUrl + entitySetName + "?fetchXml=" + encodeURI(fetchXML), false);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
successCallback(JSON.parse(this.responseText));
}
else {
errorCallback(JSON.parse(this.responseText));
}
}
}
req.send();
} function errorCallback(errorJson) {
console.log(errorJson);
if (typeof errorJson.error.message === "string") {
Xrm.Utility.alertDialog("执行FetchXml出错!" + errorJson.error.message);
}
else {
Xrm.Utility.alertDialog("执行FetchXml出错,请查看console中的输出信息!");
}
} //当执行聚合FetchXml失败,原因是因为超过AggregateQueryRecordLimit参数值的时候执行的函数
function errorCallbackHandleAggregateQueryRecordLimit(errorJson) {
console.log(errorJson);
if (typeof errorJson.error.message === "string") {
if (errorJson.error.message.indexOf('AggregateQueryRecordLimit') >= 0) {
console.log("记录数超过了AggregateQueryRecordLimit参数的值(默认五万),将执行分页查询来计数.");
executeFetchXML(entitySetName, orgFetchXML, successCallback, errorCallback);
}
else{
Xrm.Utility.alertDialog("执行FetchXml出错!" + errorJson.error.message);
}
}
else {
Xrm.Utility.alertDialog("执行FetchXml出错,请查看console中的输出信息!");
}
} //分页查询调用成功的执行函数
function successCallback(responseJson) {
if (responseJson.value.length >= 1) {
recordCount += responseJson.value.length;
console.log(recordCount + " : " + new Date());
}
if (responseJson.hasOwnProperty("@Microsoft.Dynamics.CRM.fetchxmlpagingcookie")) {
var pageInfo = getPagingCookie(responseJson["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"]);
pagingFetchXML = orgFetchXML.replace("<fetch", "<fetch page='" + pageInfo.nextPageNum + "' paging-cookie='" + pageInfo.pagingCookie + "'");
//console.log(pagingFetchXML);
executeFetchXML(entitySetName, pagingFetchXML, successCallback, errorCallback);
}
else {
console.log("分页查询完毕啦!" + " : " + new Date());
document.getElementById('countResult').innerText = "总记录条数是" + recordCount;
var tables = document.getElementsByTagName('table');
for (var i = 0; i < tables.length; i++) {
tables[i].parentNode.removeChild(tables[i]);
}
}
} //一个聚合函数可以查询出记录结果的时候显示方式
function successCallbackToShowData(responseJson) {
document.getElementById('countResult').innerText = "罗勇(www.luoyong.me)原创的程序执行完毕,结果见下表:";
var tables = document.getElementsByTagName('table');
for (var i = 0; i < tables.length; i++) {
tables[i].parentNode.removeChild(tables[i]);
}
if (responseJson.value.length >= 1) {
var recordCount = responseJson.value.length;
var cols = [];
var body = document.getElementsByTagName('body')[0];
var tbl = document.createElement('table');
var tbdy = document.createElement('tbody');
for (var i = 0; i < recordCount; i++) {
for (var propkey in responseJson.value[i]) {
if (propkey != "@odata.etag" && cols.indexOf(propkey) <= -1) {
cols.push(propkey);
}
}
}
cols.sort();
var tr = document.createElement("tr");
var td = document.createElement('td');
td.appendChild(document.createTextNode("序号"));
tr.appendChild(td);
cols.forEach(function (ele) {
td = document.createElement('td');
td.appendChild(document.createTextNode(ele));
tr.appendChild(td);
});
tbdy.appendChild(tr);
for (var i = 0; i < recordCount; i++) {
var tr = document.createElement('tr');
var td = document.createElement('td');
td.appendChild(document.createTextNode((i + 1).toString()));
tr.appendChild(td);
cols.forEach(function (ele) {
if (responseJson.value[i].hasOwnProperty(ele)) {
td = document.createElement('td');
td.appendChild(document.createTextNode(responseJson.value[i][ele]));
tr.appendChild(td);
}
else {
td = document.createElement('td');
tr.appendChild(td);
}
});
tbdy.appendChild(tr);
}
tbl.appendChild(tbdy);
body.appendChild(tbl);
}
} document.getElementById('convertit').onclick = function () {
document.getElementById('countResult').innerText = "罗勇(www.luoyong.me)原创的程序开始执行计数啦,请稍等...";
var tables = document.getElementsByTagName('table');
for (var i = 0; i < tables.length; i++) {
tables[i].parentNode.removeChild(tables[i]);
}
if (typeof GetGlobalContext != "undefined") {
crmclientcontext = GetGlobalContext();
} else {
throw new Error("引入ClientGlobalContext.js.aspx失败,请检查!");
}
clientUrl = crmclientcontext.getClientUrl() + "/api/data/v8.2/";
orgFetchXML = document.getElementById("fetchxml").value.replace(/"/gm, "'");
var entityLogicalName = orgFetchXML.match(/<entity\s?name\s?=\s?\'\w+\'/ig)[0].match(/\'.+\'/i)[0].replace(/\'/g, "");
var req = new XMLHttpRequest();
req.open("GET", clientUrl + "EntityDefinitions?$select=EntitySetName&$filter=LogicalName" + encodeURI(" eq '" + entityLogicalName + "'"), false);
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.onreadystatechange = function () {
if (this.readyState == 4 /* complete */) {
req.onreadystatechange = null;
if (this.status == 200) {
entitySetName = JSON.parse(this.responseText).value[0].EntitySetName;
}
else {
errorCallback(JSON.parse(this.responseText));
}
}
};
req.send();
aggFetchXML = orgFetchXML.replace("<fetch", "<fetch aggregate='true'");
var attrAt = aggFetchXML.search(/<attribute/g);
aggFetchXML = aggFetchXML.substring(0, attrAt + 10) + " aggregate='count' alias='recordscount'" + aggFetchXML.substring(attrAt + 10);
console.log("根据最初FetchXml改造后的聚合FetchXml =" + aggFetchXML);
executeFetchXML(entitySetName, aggFetchXML, successCallbackToShowData, errorCallbackHandleAggregateQueryRecordLimit);
};
</script>
</body>
</html>
下面我们来看看效果,首先看不足5万行记录结果如下,查询的是某个实体的状态为有效(statecode=0)的记录数,当然你要查看所有记录数,将filter元素的内容手动删除再执行查询即可。

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

当然,有个注意的地方,查询是以当前用户身份去做的,结果集也是当前用户能看到的结果集,因为权限控制,部分记录当前用户可能看不到,那么和真正的所有的符合条件的所有记录数(当前用户看到的,看不到的所有的)可能有差别,因为这个结果集只包括了当前用户有读取权限的结果集,那些没有读取权限的记录不会被统计进去。
当然,你如果用系统管理员角色用户来执行的,统计的就是真正的所有的符合条件的记录集。
无依赖简单易用的Dynamics 365实体记录数计数器并能计算出FetchXml返回的记录数的更多相关文章
- 无依赖简单易用的Dynamics 365公共视图克隆工具
本人微信公众号:微软动态CRM专家罗勇 ,回复279或者20180818可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . Dy ...
- 自定义控制台程序导出Dynamics 365实体信息到Excel中。
本人微信公众号:微软动态CRM专家罗勇 ,回复281或者20181116可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 有时 ...
- Dynamics 365 POA表记录的查询
微软动态CRM专家罗勇 ,回复313或者20190311可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . PrincipalO ...
- 介绍Dynamics 365 Customer Engagement中的备用键(alternate key)
我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...
- Dynamics 365 POA表记录的产生
微软动态CRM专家罗勇 ,回复314或者20190311可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 前面的博文 Dyna ...
- 为Dynamics 365写一个简单程序实现解决方案一键迁移
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复258或者20170627可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- 工作中经常用到github上优秀、实用、轻量级、无依赖的插件和库
原文收录在我的 GitHub博客 (https://github.com/jawil/blog) ,喜欢的可以关注最新动态,大家一起多交流学习,共同进步,以学习者的身份写博客,记录点滴. 按照格式推荐 ...
- 工作中经常用到 github 上优秀、实用、轻量级、无依赖的插件和库
原文收录在 GitHub博客 ( https://github.com/jawil/blog ) ,喜欢的可以关注最新动态,大家一起多交流学习,共同进步,以学习者的身份写博客,记录点滴. 由于gith ...
- DataAccess通用数据库访问类,简单易用,功能强悍
以下是我编写的DataAccess通用数据库访问类,简单易用,支持:内联式创建多个参数.支持多事务提交.支持参数复用.支持更换数据库类型,希望能帮到大家,若需支持查出来后转换成实体,可以自行扩展dat ...
随机推荐
- 【kafka学习之五】kafka运维:kafka操作日志设置和主题删除
一.操作日志 首先附上kafka 操作日志配置文件:log4j.properties 根据相应的需要设置日志. #日志级别覆盖规则 优先级:ALL < DEBUG < INFO <W ...
- SIP协议搭建电信级VOIP/IM运营平台--架构篇(sip集群)
移动互联网的发展为整个VOIP通信行业开拓了新的战场,一时间各类即时通信软件如雨后春筝般冒了出来,再一次创造了移动互联网的发展神话.SIP协议做为音视频通信的首选标准,应用也越来越广泛. ------ ...
- 怎么使用zepto.js的tap事件引起的探索
前言: 在使用zepto.js之前,你首先要知道它是什么?为什么要使用它?以及它和jquery有什么区别? ①:简单来说zepto是一个轻量级的针对现代高级浏览器的JavaScript库, 它与j ...
- [Swift]LeetCode653. 两数之和 IV - 输入 BST | Two Sum IV - Input is a BST
Given a Binary Search Tree and a target number, return true if there exist two elements in the BST s ...
- python bz2模块
bz2模块提供了使用bzip2算法压缩和解压缩数据一套完整的接口. bz2模块包括: 用于读写压缩文件的open()函数和BZ2File类 用于一次性压缩和解压缩的compress() 和 decom ...
- python之读取配置文件模块configparser(一)基本操作
configparser模块是读取类ini文件使用,其有固定的读取格式如下: [section1] option11 = value11 option12 = value12 .... [sectio ...
- 死磕 java集合之CopyOnWriteArrayList源码分析
欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过 ...
- Visual Studio 2017中使用SourceLink调试ASP.NET Core源码
背景 当我们在学习ASP.NET Core或者调试ASP.NET Core程序的时候,有时候需要调试底层代码,但是当我们在Visual Studio中调试程序的时候,由于一些基础库或者第三方库缺少pd ...
- C++版 - 剑指offer面试题28: 字符串的排列
题目: 字符串的排列 热度指数:5777 时间限制:1秒 空间限制:32768K 本题知识点: 字符串 题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出 ...
- Android--加载大分辨率图片到内存
前言 在使用ImageView显示图片的时候,直接加载一个图片资源到内存中,经常会出现内存溢出的错误,这是因为有些图片的分辨率比较高,把它直接加载到内存中之后,会导致堆内存溢出的问题.这篇博客就来讲解 ...