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

项目升级到Dynamics 365以后,过了些时间,有的账号反馈使用很慢,甚至同一个地方同一台电脑用不同的账号特别是进行数据修改的时候会发现有明显的速度差别,Why?

初步的表象发现是POST 到这个请求 /AppWebServices/InlineEditWebService.asmx 比较慢,这个是系统标准的东西,就算是有问题,我们能做的可能也有限。奇怪的是我们使用同样业务部门同样角色的账号处理类似单据的时候并不能重现此问题,而且通过SQL Profiler抓取的SQL执行语句也没有发现执行时间很长的SQL。

有问题就有解决问题的方法,大神们日夜分析,发现卡顿的主要原因是执行部分SQL后会卡顿,等待然后再执行,这样导致的总体时间很长,整个过程甚至能超过20秒。再次详细分析,发现每次这个卡顿都会又一次查询 BusinessProcessflowInstanceBase 这个表,奇怪啊,我们并没有使用业务流程。后来微软技术支持中心向更加广泛的范围求助,终于找到答案,原来是Dynamics 365开始,CRM中的一条记录可能有多个活动业务流程,每个人根据其权限等显示的业务流程又不尽相同。所以会查询这个表,当然在查询这个表之前会先查询这条记录相关的MRU(Most Recently Used List,最近访问列表),但是如果这个MRU非常大,就会非常耗时导致等待。

那么这么MRU怎么看?当然你可以通过SQL直接来看,这个MUR跟用户和实体的Typecode有关,下面是查看某个用户的所有MRU:

 select ueus.RecentlyViewedXml,
len(ueus.RecentlyViewedXml) as RecentlyViewedXmlLength,
sysuser.FullName,
entity.LogicalName
from UserEntityUISettings ueus
left join SystemUserBase sysuser on ueus.OwnerId = sysuser.SystemUserId
left join entity on ueus.ObjectTypeCode = entity.ObjectTypeCode

我看到我这个账号经常使用ly_test这个实体,发现其RecentlyViewedXml大小也有 8832 字节,如果你用的狠的话,这个数字可能会超过1Mb。这个里面的内容我简化下如下:

 <RecentlyViewedEntityData etc="10007">
<RecentlyViewedItem>
<Type>0</Type>
<ObjectId>{5DA888D6-06A3-E611-816B-000D3A80C8B8}</ObjectId>
<EntityTypeCode>10007</EntityTypeCode>
<DisplayName>罗勇测试</DisplayName>
<Title>批量操作创建的罗勇测试记录</Title>
<Action></Action>
<IconPath></IconPath>
<PinStatus>false</PinStatus>
<ProcessInstanceId></ProcessInstanceId>
<ProcessId></ProcessId>
<LastAccessed>11/05/2016 03:20:58</LastAccessed>
</RecentlyViewedItem>
</RecentlyViewedEntityData>

怎么解决?当然你可以通过组织服务来清除这个字段的值,也可以直接用SQL来处理,比如将长度大于1000的清除:

 update UserEntityUISettings
set RecentlyViewedXml=null
where len(RecentlyViewedXml)>=1000

用SQL来清除这个MRU有个弊端就是需要重启IIS,因为它在服务器端有缓存。

当然也可以写程序来清除,目前没有看到界面上提供手动清除MRU的地方:

                 Console.WriteLine("本程序用于清理你输入账号的最近访问记录。");
string userName = string.Empty;
string passWord = string.Empty;
Console.WriteLine("请输入登录的用户名,输入完毕后按回车键确认:");
userName = Console.ReadLine().ToString().Trim();
Console.WriteLine("请输入登录的密码,输入完毕后按回车键确认:");
while (true)
{
ConsoleKeyInfo ck = Console.ReadKey(true);
if (ck.Key != ConsoleKey.Enter)
{
if (ck.Key != ConsoleKey.Backspace)
{
passWord += ck.KeyChar.ToString();
Console.Write("*");
}
else
{
Console.Write("\b \b");
}
}
else
{
break;
}
}
ClientCredentials cc = new ClientCredentials();
cc.UserName.UserName = userName;
cc.UserName.Password = passWord;
OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(new Uri("https://demo.luoyong.me/XRMServices/2011/Organization.svc"),
null, cc, null);
WhoAmIRequest whoReq = new WhoAmIRequest();
WhoAmIResponse whoRep = orgSvc.Execute(whoReq) as WhoAmIResponse;
var userEntity = orgSvc.Retrieve("systemuser", whoRep.UserId, new ColumnSet("fullname"));
Console.WriteLine(string.Format("登录成功,欢迎{0},继续操作请输入y!", userEntity.GetAttributeValue<string>("fullname")));
var input = Console.ReadLine().ToString().ToUpper();
if (input == "Y")
{
QueryExpression qe = new QueryExpression("userentityuisettings");
qe.ColumnSet = new ColumnSet("recentlyviewedxml");
qe.Criteria.AddCondition("ownerid", ConditionOperator.Equal, whoRep.UserId);
var usersettings = orgSvc.RetrieveMultiple(qe);
if (usersettings.Entities.Count >= )
{
Console.WriteLine("共找到" + usersettings.Entities.Count + "条最近访问记录!");
foreach (var item in usersettings.Entities)
{
item["recentlyviewedxml"] = null;
orgSvc.Update(item);
}
}
}
else
{
Console.WriteLine("你选择了取消操作!");
}
Console.WriteLine("程序运行完成!");
Console.ReadKey();

据我对这个MRU的观察,MRU的缓存在IIS上有缓存,用户登录会从服务器上获取缓存,本地并不是通过浏览器来缓存,会在用户退出登录时候将访问记录写入到服务器端。所以仅仅依靠程序或者SQL来清除MRU还不管用,需要配合IIS重启。

系统在这个设计上可能欠缺考虑,比如针对某个实体的MRU如果超过一定的数量采用先进先出的方法自动清除掉前面的访问记录,毕竟保存那么多也没有很大的用处,除非你要做访问记录的审核。也可以考虑再某个地方让用户自行清理。希望微软Dynamics CRM产品组早日就此问题提出根本解决方案。

05-08 15:21