注册 登录  
 加关注

网易博客网站关停、迁移的公告:

将从2018年11月30日00:00起正式停止网易博客运营
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

微软MVP罗勇的博客

微软MVP罗勇,www.luoyong.me

 
 
 

日志

 
 

实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值  

2015-09-24 18:12:48|  分类: CRM插件 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复156或者20150924可方便获取本文,同时可以在第一时间得到我发布的最新的博文信息,follow me!
前面的 插件系列博客教程 讲述了新增记录(Create),删除记录(Delete) 和更新记录(Update)插件实例,没有涉及到更改记录状态(即更改状态和状态描述字段值)消息时候注册插件。今天的博文就讲述如何通过插件侦听到这个消息,并获取更新前和更新后的值。我们知道Dynamics CRM中几乎每个实体(含所有新建的自定义实体)都有两个字段,分别是statecode和statuscode,我们以一个自定义实体的元数据来看看:
SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客

从上面的图可以知道,当statecode = 0的时候是显示可用,statecode=1的时候是显示停用,该字段的显示名称是状态。而statuscode=1的时候是显示可用,statuscode=2的时候显示停用,该字段的显示名称是状态描述。这两个字段的字段类型并不是我们常用的 选项集 这种字段类型,statecode字段的类型是StateType,statuscode字段的类型是StatusType,这两种字段类型我们定义新字段的时候不能选择到,也就是不能使用,当然获取这两个字段的值的时候和获取选项集类型字段的值方法是一样的。我们可以简单理解statecode为分类中的大类,而statuscode为分类中的小类,设置分类的时候,大类和小类要匹配,也就是选择了某个大类,小类也就是只能是这个大类下面的小类,事实上也是如此。

我们做客制化的时候可以增加或者删除statuscode字段的可选项,但是不能更改statecode的可选项。当然了,有些标准实体这两个字段的可选值丰富些,比如订单实体的StateCode就有如下五种可选项:
值: 0, 显示值: 可用
: 1, 显示值: 已提交
: 2, 显示值: 已取消
: 3, 显示值: 已完成
: 4, 显示值: 已开发票

它的StatusCode则有如下其中可选项:
: 3, 显示值: 正在进行
: 4, 显示值: 无现金
: 1, 显示值: 新建
: 2, 显示值: 挂起
: 100001, 显示值: 完成
: 100002, 显示值: 部分
: 100003, 显示值: 已开发票

有一点值得注意的是,自定义实体的状态和状态说明字段的架构名称(Schema Name)和逻辑名称(Logical Name)都是小写,分别是statecode和statuscode,而至少部分自定义实体,比如订单实体的状态和状态说明字段的架构名称是大小写混合的,架构名称分别是StateCode和StatusCode,撰写代码(特别是调用OData终结点的时候)的时候要注意。

截至本博文写作为止,Dynamics CRM 2015更新这两个特殊字段的方法可以通过代码执行 SetStateRequest 消息来更改。在CRM界面上,对于自定义实体而言,则是通过命令栏的 激活 (Activate) 和 停用(Deactivate) 按钮来改变这组字段的值。
SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
对于部分标准实体,改变这对特殊字段的值则不一定是通过激活或者停用来做,可能是另外的按钮,比如案例实体则是通过 解决案例(执行CloseIncidentRequest 消息) 、 取消案例 或者 重新激活案例 来做。
SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
还比如潜在顾客实体通过 授予资格 按钮来改变,应该执行的消息是 QualifyLeadRequest 消息。
SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
一开始我也以为是要在SetState消息中注册插件,首先要确认这个实体是否支持在这个消息中注册插件,对于标准实体,SDK\Message-entity support for plug-ins.xlsx 中可以查看,我这里筛选了Primary Entity 为Incident 的实体中支持插件的消息如下,可以发现有SetState消息,所以插件可以注册在SetState消息中。对于自定义实体,应该是都支持的。PS:案例实体在英文中显示的是Case,所以有的人想当然的认为这个实体的架构名称也是Case,但是实际上不是如此,它的架构名称是Incident。
SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
因为标准实体有点特殊,所以留待以后的博文讲解。我这篇博文先讲通用的,所以我在自定义的测试实体注册了插件如下:在SetState消息的Pre-Operation阶段注册了插件,并且使用了一个Pre Image Alias映像用来获取更改之前statecode 和 statuscode 字段的值。
SetState消息插件实例及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
使用的代码如下:

try
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}

IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
Entity preImageEntity = (context.PreEntityImages != null && context.PreEntityImages.Contains(this.preImageAlias)) ? context.PreEntityImages[this.preImageAlias] : null;

// TODO: Implement your custom Plug-in business logic.
OptionSetValue preStateCode = preImageEntity.Contains("statecode") ? preImageEntity.GetAttributeValue<OptionSetValue>("statecode") : null;
OptionSetValue preStatusCode = preImageEntity.Contains("statuscode") ? preImageEntity.GetAttributeValue<OptionSetValue>("statuscode") : null;
if (context.InputParameters.Contains("EntityMoniker") && context.InputParameters["EntityMoniker"] is EntityReference)
{
EntityReference currentEntityRef = (EntityReference)context.InputParameters["EntityMoniker"];
OptionSetValue postStateCode = context.InputParameters.Contains("State") ? (OptionSetValue)context.InputParameters["State"] : null;
OptionSetValue postStatusCode = context.InputParameters.Contains("Status") ? (OptionSetValue)context.InputParameters["Status"] : null;
var noteText = new StringBuilder("SetState消息执行前的statecode字段值为:");
noteText.Append(preStateCode == null ? "null" : preStateCode.Value.ToString());
noteText.Append(";statuscode字段值为:");
noteText.Append(preStatusCode == null ? "null" : preStatusCode.Value.ToString());
noteText.Append(";SetState消息执行时要将的statecode字段值变更为:");
noteText.Append(postStateCode == null ? "null" : postStateCode.Value.ToString());
noteText.Append(";statuscode字段值变更为:");
noteText.Append(postStatusCode == null ? "null" : postStatusCode.Value.ToString());
Entity noteEntity = new Entity("annotation");
noteEntity["isdocument"] = false;
noteEntity["notetext"] = noteText.ToString();
noteEntity["objectid"] = currentEntityRef;
noteEntity["subject"] = "微软MVP罗勇测试SetState消息";
service.Create(noteEntity);
}
else
{
throw new Exception(@"输入参数中不包括EntityMoniker参数,或者包括了EntityMoniker参数但是其类型不是实体引用");
}
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException(@"执行消息SetState的Pre阶段插件出错:" + ex.Message + ex.StackTrace);
}

部署后我去测试下,在CRM界面上将一条测试实体的记录禁用或者启用,都不见触发该插件执行,囧。查找资料可以得知应该在 SetStateDynamicEntity 消息注册插件,所以我注册了一个类似如下的插件:在SetStateDanamicEntity消息的Pre-Operation阶段注册了插件,并且使用了一个Pre Image Alias映像用来获取更改之前statecode 和 statuscode 字段的值。
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
使用的代码如下:

try
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}

IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
Entity preImageEntity = (context.PreEntityImages != null && context.PreEntityImages.Contains(this.preImageAlias)) ? context.PreEntityImages[this.preImageAlias] : null;

// TODO: Implement your custom Plug-in business logic.
OptionSetValue preStateCode = preImageEntity.Contains("statecode") ? preImageEntity.GetAttributeValue<OptionSetValue>("statecode") : null;
OptionSetValue preStatusCode = preImageEntity.Contains("statuscode") ? preImageEntity.GetAttributeValue<OptionSetValue>("statuscode") : null;
if (context.InputParameters.Contains("EntityMoniker") && context.InputParameters["EntityMoniker"] is EntityReference)
{
EntityReference currentEntityRef = (EntityReference)context.InputParameters["EntityMoniker"];
OptionSetValue postStateCode = context.InputParameters.Contains("State") ? (OptionSetValue)context.InputParameters["State"] : null;
OptionSetValue postStatusCode = context.InputParameters.Contains("Status") ? (OptionSetValue)context.InputParameters["Status"] : null;
var noteText = new StringBuilder("SetStateDynamicEntity消息执行前的statecode字段值为:");
noteText.Append(preStateCode == null ? "null" : preStateCode.Value.ToString());
noteText.Append(";statuscode字段值为:");
noteText.Append(preStatusCode == null ? "null" : preStatusCode.Value.ToString());
noteText.Append(";SetStateDynamicEntity消息执行时要将的statecode字段值变更为:");
noteText.Append(postStateCode == null ? "null" : postStateCode.Value.ToString());
noteText.Append(";statuscode字段值变更为:");
noteText.Append(postStatusCode == null ? "null" : postStatusCode.Value.ToString());
Entity noteEntity = new Entity("annotation");
noteEntity["isdocument"] = false;
noteEntity["notetext"] = noteText.ToString();
noteEntity["objectid"] = currentEntityRef;
noteEntity["subject"] = "微软MVP罗勇测试SetStateDynamicEntity消息";
service.Create(noteEntity);
}
else
{
throw new Exception(@"输入参数中不包括EntityMoniker参数,或者包括了EntityMoniker参数但是其类型不是实体引用");
}
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException(@"执行消息SetStateDynamicEntity的Pre阶段插件出错:" + ex.Message + ex.StackTrace);
}
}

然后我来测试下这个结果,将一条记录先 停用,再激活,发现产生了如下的结果:
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
可以看到在Pre阶段通过映像(Image)获取到的statecode和statuscode是正确的,而获取的要设置的值中statecode是准确的,而statuscode则是 -1,囧。可以看到没有触发我注册在 SetState 消息中的插件代码。

那你可能会问,如果我用代码执行 SetStateRequest 消息会触发我注册在SetState 消息中的插件代码吗?我们去验证下,我使用了简单的验证代码如下:

var orgService = GetOrganizationService();
var setStateRequest = new SetStateRequest()
{
EntityMoniker = new EntityReference("new_test",new Guid("565713BC-9161-E511-80D6-000D3A803868")),
State = new OptionSetValue(1),//inactive
Status = new OptionSetValue(2)//inactive
};
orgService.Execute(setStateRequest);

执行以后产生的注释如下,比较第一条和第三条可以得知当通过执行SetStateRequest 消息执行的时候传递了Status值过去,也能准确的获取到,而通过界面上的 停用 或者 激活 按钮 则获取到的 Status 值总是 -1 。也可以看到并没有执行注册在 SetState 消息中的插件代码,看来插件注册到 SetState 消息中是没有用的,这个消息能注册插件可能是历史遗留问题
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
还验证一个问题,界面上的 停用 和 激活 传递过来的statuscode 为 -1 到底代表什么意思,为了,我客制化了实体的 statuscode 字段,增加了如下两个可选项,然后将这两个状态字段拉动放到表单的页脚:
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
  
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
然后我去界面上使用 停用 按钮,弹出对话框如下,可以选择,我这里使用默认的 停用:
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
产生的内容如下,可以知道这次注册在 SetStateDynamicEntity 消息中的插件代码获取了准确的statuscode值。
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
我在点击  激活 按钮,它没有像点击 停用 那样弹出对话框让我选择一个状态,产生的内容如下。应该是没有弹出对话框让我选择的缘故,所以这里获取不到要设置的statuscode字段的值,而设置的 状态描述(statuscode) 字段值是 可用 这个状态(statecode) 下的某个值,我测试了一下,不知道他用的是什么方法来决定取哪个,这个比较玄。
实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
 
 如果我把 状态 和 状态描述 字段放到表单的主题中,发现状态为 可用 的时候,状态描述字段是可以更改值得,当然用于的值也仅限于状态为 可用 时候的 状态描述 可选列表。当状态为 停用 的时候整个记录都是只读的,自然 状态描述 字段也是不可以更改。

素格格新疆特产店--做最好新疆特产的搬运工。 本店由博主的新疆老婆开设,搬运各种最好的新疆特产,是自用,送礼,年货,孝敬长辈特别是(岳)父母的首选!素格格新疆特产店淘宝网址是 http://sugege.taobao.com 欢迎自助购买。

实体状态字段(statecode,statuscode)介绍、插件注册及获取更改状态前后的值 - 罗勇 - 微软MVP-罗勇的博客
  评论这张
 
阅读(903)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018