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

 
为了方便说明,我首先创建一个自定义工作流活动,使用的代码如下。
using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using Microsoft.Xrm.Sdk.Workflow;
using Microsoft.Xrm.Sdk;
using System.Reflection; namespace CrmVSSolution.Workflow
{
public sealed class PostTestUpdate : CodeActivity
{
protected override void Execute(CodeActivityContext executionContext)
{
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
tracingService.Trace("进入自定义工作流活动CrmVSSolution.Workflow.PostTestUpdate");
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService orgService = serviceFactory.CreateOrganizationService(context.UserId);
StringBuilder sb = new StringBuilder();
PropertyInfo[] properties = context.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo proInfo in properties)
{
sb.Append(proInfo.Name);
sb.Append(" = ");
if (proInfo.CanRead)
{
sb.Append(proInfo.GetValue(context, null));
}
else
{
sb.Append("Cannot read value");
}
sb.Append("(");
sb.Append(proInfo.PropertyType.ToString());
sb.Append(");");
sb.Append("\n");
}
tracingService.Trace("工作流中context的所有参数如下:\n");
tracingService.Trace(sb.ToString());
sb.Clear();
foreach(var item in context.InputParameters)
{
sb.Append(item.Key);
sb.Append(" = ");
if (item.Value != null)//注意有的属性的值为null,比如InputArguments
{
sb.Append(item.Value.ToString());
}
else
{
sb.Append("null");
}
sb.Append("\n");
}
tracingService.Trace("工作流中context.InputParameter的参数如下:\n");
tracingService.Trace(sb.ToString());
sb.Clear();
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
sb.Append("工作流中包括了Target参数,它是Entity类型.\n");
var currentEntity = context.InputParameters["Target"] as Entity;
sb.Append("Target参数包括如下属性:\n");
foreach (var attr in currentEntity.Attributes)
{
sb.Append(attr.Key);
sb.Append(" = ");
if (attr.Value != null)//注意有的属性值为null,比如modifiedonbehalfby,所以要加上判断
{
sb.Append(attr.Value.ToString());
}
else
{
sb.Append("null");
}
sb.Append("\n");
}
tracingService.Trace(sb.ToString());
}
tracingService.Trace("结束自定义工作流活动CrmVSSolution.Workflow.PostTestUpdate");
throw new InvalidWorkflowException("有时候不抛出异常不行啊!");
}
}
}
然后使用 SDK\Tools\PluginRegistration\PluginRegistration.exe 来注册自定义工作流活动,连接方式如下,我这里是做了IFD的我自己的CRM实验环境。
 
点击 Register > Register New Assembly,注意注册之前,你的程序集要签名哦。

注册如下,Dynamics 365 Online不支持注册到None中,所以Step 3要选择 Sandbox。但是我的代码中使用了反射来读取参数值,使用的也是本地部署的Dynamics 365,所以注册到None中,注册到Sandbox中的程序集具有的权限要小得多。点击下面的Register Selected Plugins 按钮。电脑分辨率低的童鞋注意,可能要用tab键跳转到这个按钮。

一会儿告诉我注册成功。
根据需要,最好是调整下这个自定义工作流活动的WorkflowActivityGroupName和Name,我这里调整如下。然后点击保存按钮保存就可以了。这个保存按钮好小啊。

然后我们在工作流中就可以使用了,我这里做一个工作流如下,监控的是单行文本字段的变更。我这也也勾选了 作为按需工作流,所以是可以手工启动运行的。

当然需要激活工作流,因为我要看的东西都是写在工作流的trace里面,所以我还去 设置 > 管理 > 系统设置 > 自定义里面,将启用插件跟踪日志的日志记录这个选项设置为所有。
变更字段值之后我们去看插件日志,在设置>自定义>插件跟踪日志里面查看。不过还真有时候看不到,一般是因为你注册在None中,而不是注册在Sandbox中。为了能看到跟踪信息,我在自定义工作流活动最后抛出异常,这样在工作流的详细信息中肯定可以看到Trace到的消息。我这里先做自动触发的来看看,我更新了单行文本字段值和多行文本字段的值来触发这个工作流,看到的消息如下:
进入自定义工作流活动CrmVSSolution.Workflow.PostTestUpdate
工作流中context的所有参数如下:
 
PreEntityImages = Microsoft.Xrm.Sdk.EntityImageCollection(Microsoft.Xrm.Sdk.EntityImageCollection);
PostEntityImages = Microsoft.Xrm.Sdk.EntityImageCollection(Microsoft.Xrm.Sdk.EntityImageCollection);
InputParameters = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
OutputParameters = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
SharedVariables = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
WorkflowCategory = 0(System.Int32);
Mode = 1(System.Int32);
LegacyContext = Microsoft.Crm.Workflow.LegacyWorkflowContext(Microsoft.Crm.Workflow.ILegacyWorkflowContext);
OperationStatus = Microsoft.Crm.Workflow.WorkflowOperationInProgressResult(Microsoft.Crm.IGenericHandlerResult);
PluginTypeCache = Microsoft.Crm.Caching.PluginTypeCache(Microsoft.Crm.Caching.PluginTypeCache);
PrimaryEntityName = ly_test(System.String);
PrimaryEntityId = b707de1b-cf99-e611-8161-000d3a80c8b8(System.Guid);
MessageName = Update(System.String);
RequestId = (System.Nullable`1[System.Guid]);
UserId = e9cd027f-26a3-e511-80c6-000d3a807ec7(System.Guid);
InitiatingUserId = e9cd027f-26a3-e511-80c6-000d3a807ec7(System.Guid);
CorrelationId = 7d92631b-2f5f-4d71-b6c2-16608ba82b0c(System.Guid);
Depth = 1(System.Int32);
IsolationMode = 1(System.Int32);
OwningExtension = Microsoft.Xrm.Sdk.EntityReference(Microsoft.Xrm.Sdk.EntityReference);
BusinessUnitId = 487cdd4b-26a3-e511-80c6-000d3a807ec7(System.Guid);
IsExecutingOffline = False(System.Boolean);
IsOfflinePlayback = False(System.Boolean);
IsInTransaction = False(System.Boolean);
OperationId = e697feec-b901-e711-8178-000d3a80c8b8(System.Guid);
OrganizationId = bd2a5c49-6b08-4eda-8a15-84159d9fd349(System.Guid);
OrganizationName = Demo(System.String);
SecondaryEntityName = (System.String);
OperationCreatedOn = 3/5/2017 3:39:32 PM(System.DateTime);
StageName = (System.String);
WorkflowMode = 0(System.Int32);
ExtensionParameters = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
PrimaryEntityImage = Microsoft.Xrm.Sdk.Entity(Microsoft.Xrm.Sdk.Entity);
IsCrmUIWorkflow = True(System.Boolean);
IsAutoDeleteSet = False(System.Boolean);
IsLoggingEnabled = True(System.Boolean);
GoingIdle = False(System.Boolean);
WorkflowStageProperty = Microsoft.Crm.Workflow.WorkflowStageProperty(Microsoft.Crm.Workflow.WorkflowStageProperty);
WorkflowLogsProperty = Microsoft.Crm.Workflow.WorkflowLogsProperty(Microsoft.Crm.Workflow.WorkflowLogsProperty);
CorrelationToken = Microsoft.Crm.Sdk.CorrelationToken(Microsoft.Crm.Sdk.CorrelationToken);
EntityDependencies = System.Collections.ObjectModel.Collection`1[Microsoft.Crm.Workflow.EntityDependencyBase](System.Collections.ObjectModel.Collection`1[Microsoft.Crm.Workflow.EntityDependencyBase]);
WorkflowTracingService = Microsoft.Crm.Workflow.WorkflowTracingService(Microsoft.Crm.Workflow.WorkflowTracingService);
SdkService = (Microsoft.Crm.Workflow.IWorkflowSdkServiceFactory);
Event = Microsoft.Crm.Asynchronous.AsyncEvent(Microsoft.Crm.Asynchronous.IGenericEventData);
ChildWorkflowInstanceId = 00000000-0000-0000-0000-000000000000(System.Guid);
InstanceState = Microsoft.Crm.Workflow.AsyncWorkflowInstanceState(Microsoft.Crm.Workflow.WorkflowInstanceStateBase);
ProxyTypesAssembly = (System.Reflection.Assembly);
CallerOrigin = Microsoft.Crm.Sdk.ApplicationOrigin(Microsoft.Crm.Sdk.CallerOrigin);
CorrelationUpdateTime = 3/5/2017 3:39:32 PM(System.DateTime);
TransactionContextId = e697feec-b901-e711-8178-000d3a80c8b8(System.Guid);
ParentPluginExecutionId = 00000000-0000-0000-0000-000000000000(System.Guid);
Arguments = (Microsoft.Xrm.Sdk.Workflow.ArgumentsCollection);
ConversionContext = Microsoft.Crm.BusinessEntities.ConversionContext(Microsoft.Crm.BusinessEntities.ICrmConversionContext);
LegacyConversionContext = Microsoft.Crm.BusinessEntities.ConversionContext(Microsoft.Crm.BusinessEntities.ICrmConversionContext);
 
工作流中context.InputParameter的参数如下:
 
Target = Microsoft.Xrm.Sdk.Entity
ConcurrencyBehavior = Default
 
工作流中包括了Target参数,它是Entity类型.
Target参数包括如下属性:
ly_singlelinetext = 新的单行文本字段值
ly_testid = b707de1b-cf99-e611-8161-000d3a80c8b8
modifiedon = 3/5/2017 3:39:28 PM
modifiedby = Microsoft.Xrm.Sdk.EntityReference
modifiedonbehalfby = null
 
结束自定义工作流活动CrmVSSolution.Workflow.PostTestUpdate

然后我手工启动工作流来触发该工作流,得到的结果如下:

进入自定义工作流活动CrmVSSolution.Workflow.PostTestUpdate
工作流中context的所有参数如下:
 
PreEntityImages = Microsoft.Xrm.Sdk.EntityImageCollection(Microsoft.Xrm.Sdk.EntityImageCollection);
PostEntityImages = Microsoft.Xrm.Sdk.EntityImageCollection(Microsoft.Xrm.Sdk.EntityImageCollection);
InputParameters = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
OutputParameters = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
SharedVariables = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
WorkflowCategory = 0(System.Int32);
Mode = 1(System.Int32);
LegacyContext = Microsoft.Crm.Workflow.LegacyWorkflowContext(Microsoft.Crm.Workflow.ILegacyWorkflowContext);
OperationStatus = Microsoft.Crm.Workflow.WorkflowOperationInProgressResult(Microsoft.Crm.IGenericHandlerResult);
PluginTypeCache = Microsoft.Crm.Caching.PluginTypeCache(Microsoft.Crm.Caching.PluginTypeCache);
PrimaryEntityName = ly_test(System.String);
PrimaryEntityId = b707de1b-cf99-e611-8161-000d3a80c8b8(System.Guid);
MessageName = ExecuteWorkflow(System.String);
RequestId = (System.Nullable`1[System.Guid]);
UserId = e9cd027f-26a3-e511-80c6-000d3a807ec7(System.Guid);
InitiatingUserId = e9cd027f-26a3-e511-80c6-000d3a807ec7(System.Guid);
CorrelationId = 32f4fcb1-425c-453b-8eb1-902b328635bf(System.Guid);
Depth = 1(System.Int32);
IsolationMode = 1(System.Int32);
OwningExtension = Microsoft.Xrm.Sdk.EntityReference(Microsoft.Xrm.Sdk.EntityReference);
BusinessUnitId = 487cdd4b-26a3-e511-80c6-000d3a807ec7(System.Guid);
IsExecutingOffline = False(System.Boolean);
IsOfflinePlayback = False(System.Boolean);
IsInTransaction = False(System.Boolean);
OperationId = 8597e77d-ba01-e711-8178-000d3a80c8b8(System.Guid);
OrganizationId = bd2a5c49-6b08-4eda-8a15-84159d9fd349(System.Guid);
OrganizationName = Demo(System.String);
SecondaryEntityName = (System.String);
OperationCreatedOn = 3/5/2017 3:43:35 PM(System.DateTime);
StageName = (System.String);
WorkflowMode = 0(System.Int32);
ExtensionParameters = Microsoft.Xrm.Sdk.ParameterCollection(Microsoft.Xrm.Sdk.ParameterCollection);
PrimaryEntityImage = Microsoft.Xrm.Sdk.Entity(Microsoft.Xrm.Sdk.Entity);
IsCrmUIWorkflow = True(System.Boolean);
IsAutoDeleteSet = False(System.Boolean);
IsLoggingEnabled = True(System.Boolean);
GoingIdle = False(System.Boolean);
WorkflowStageProperty = Microsoft.Crm.Workflow.WorkflowStageProperty(Microsoft.Crm.Workflow.WorkflowStageProperty);
WorkflowLogsProperty = Microsoft.Crm.Workflow.WorkflowLogsProperty(Microsoft.Crm.Workflow.WorkflowLogsProperty);
CorrelationToken = Microsoft.Crm.Sdk.CorrelationToken(Microsoft.Crm.Sdk.CorrelationToken);
EntityDependencies = System.Collections.ObjectModel.Collection`1[Microsoft.Crm.Workflow.EntityDependencyBase](System.Collections.ObjectModel.Collection`1[Microsoft.Crm.Workflow.EntityDependencyBase]);
WorkflowTracingService = Microsoft.Crm.Workflow.WorkflowTracingService(Microsoft.Crm.Workflow.WorkflowTracingService);
SdkService = (Microsoft.Crm.Workflow.IWorkflowSdkServiceFactory);
Event = Microsoft.Crm.Asynchronous.AsyncEvent(Microsoft.Crm.Asynchronous.IGenericEventData);
ChildWorkflowInstanceId = 00000000-0000-0000-0000-000000000000(System.Guid);
InstanceState = Microsoft.Crm.Workflow.AsyncWorkflowInstanceState(Microsoft.Crm.Workflow.WorkflowInstanceStateBase);
ProxyTypesAssembly = (System.Reflection.Assembly);
CallerOrigin = Microsoft.Crm.Sdk.ApplicationOrigin(Microsoft.Crm.Sdk.CallerOrigin);
CorrelationUpdateTime = 3/5/2017 3:43:35 PM(System.DateTime);
TransactionContextId = 8597e77d-ba01-e711-8178-000d3a80c8b8(System.Guid);
ParentPluginExecutionId = 00000000-0000-0000-0000-000000000000(System.Guid);
Arguments = (Microsoft.Xrm.Sdk.Workflow.ArgumentsCollection);
ConversionContext = Microsoft.Crm.BusinessEntities.ConversionContext(Microsoft.Crm.BusinessEntities.ICrmConversionContext);
LegacyConversionContext = Microsoft.Crm.BusinessEntities.ConversionContext(Microsoft.Crm.BusinessEntities.ICrmConversionContext);
 
工作流中context.InputParameter的参数如下:
 
EntityId = b707de1b-cf99-e611-8161-000d3a80c8b8
WorkflowId = 6bebc426-f722-4b64-ae5d-0da379f8a8c4
InputArguments = null
 
结束自定义工作流活动CrmVSSolution.Workflow.PostTestUpdate
我们可以看到一些东西:
1. 自动启动的工作流,MessageName是触发这个工作流运行的消息,比如第一个是Update。而手工启动的工作流,MessageName则是固定的ExecuteWorkflow。
 
2. 自动启动的工作流,context.InputParameter中包括了Target参数,该参数是Entity类型,该Entity包括的属性中包括了触发该该工作流的属性的值。而如果是手动运行工作流的话,则context.InputParameter中不包括Target参数,
包括的是EntityId参数。所以如果一个工作流,既要可以自动触发,也允许手动运行,写代码时候不要认为context.InputParameter中一定包括了Target参数,这样会导致空引用异常。如果要拿实体名称和当前记录的ID,使用 context.PrimaryEntityName 和 context.PrimaryEntityId 即可。
 
3.虽然自动启动的工作流,context.InputParameter中包括了Target参数,该参数是Entity,但是并不会包括所有的变更属性的值(这和插件不一样),只会包括监控的字段的值。要获取触发工作流后变更后属性(字段)的值,如果是自动触发,则最靠谱的是context.InputParameter中Target实体的该属性的值,当然也要监控这个字段才行。通过工作流参数传递过来的变化字段的值,或者在自定义工作里活动中查询变化字段的值则是工作流运行时刻该字段的值。但是对于有值更改为无值,在自定义工作流活动中查询到的是最新的值也就是无值,而参数传递过来的却是变化之前的值,奇怪。如果要做变化前后的对比就只有使用插件了,当然审核(audit)功能也能记载下变化前后的值。如果要以最新的该实体字段的值来做,最好的是在工作流活动中查询一遍。
 
4.对于自动启动和手工运行的工作流,context.InitiatingUserId 拿到的始终是触发(启动)该工作流运行的操作者的ID,而context.UserId对于自动启动运行的工作流拿到的则是工作流负责人的ID,这个工作流负责人一般是具有系统管理员角色的超级用户。context.UserId对于手工启动运行的工作流拿到的是运行该工作流的用户的ID。所以在获取组织服务的时候我建议使用IOrganizationService orgService = serviceFactory.CreateOrganizationService(context.UserId); 这样容易避免因为触发该工作流的用户权限不够而带来工作流运行失败的问题。
 

Dynamics 365中自定义工作流活动获取的上下文分析及注意事项的更多相关文章

  1. Dynamics 365中自定义工作流活动更新了输入输出参数后获取的方法

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

  2. 自定义工作流活动报错:您无法登陆系统。原因可能是您的用户记录或您所属的业务部门在Microsoft Dynamics 365中已被禁用。

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

  3. Dynamics 365工作流报错:您无法登陆系统。原因可能是您的用户记录或您所属的业务部门在Microsoft Dynamics 365中已被禁用。

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

  4. Dynamics 365 CE的插件/自定义工作流活动中调用Web API示例代码

    微软动态CRM专家罗勇 ,回复325或者20190428可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! 现在Web API越来越流行,有时候为了程序更加健壮,需要在插件 ...

  5. Dynamics 365 CE将自定义工作流活动程序集注册到磁盘并引用其他类库

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

  6. Dynamics 365中开发和注册插件介绍

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

  7. 自定义工作流活动运行产生System.Security.SecurityException

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

  8. Dynamics 365中的应用程序介绍

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

  9. 将Dynamics 365中的用户及其角色、角色导出到Excel中

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

随机推荐

  1. PHP GZIP压缩+BASE64

    <?php $str = ' {"pf":"AC25c","dt":"2017-02-04 09:49:49",& ...

  2. FFmpeg 学习(五):FFmpeg 编解码 API 分析

    在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...

  3. [Swift]LeetCode225. 用队列实现栈 | Implement Stack using Queues

    Implement the following operations of a stack using queues. push(x) -- Push element x onto stack. po ...

  4. [Swift]LeetCode904. 水果成篮 | Fruit Into Baskets

    In a row of trees, the i-th tree produces fruit with type tree[i]. You start at any tree of your cho ...

  5. the python challenge闯关记录(9-16)

    9 第九关 是一张图,上面有很多的黑点,查看网页源代码发现了上一关的提示: 还发现了一大串的数字 感觉又是一个使用PIL库进行图像处理的题,百度后知道要将这些点连接起来并重新画图.但是不能在原始图上修 ...

  6. 巡风源码阅读与分析---AddPlugin()方法

    文件位置:view/view.py AddPlugin() # 新增插件异步 @app.route('/addplugin', methods=['get', 'post']) @logincheck ...

  7. tensorflow、cuda、cudnn之间的版本对应关系

    原文链接 tensorflow-gpu v1.9.0 | cuda9.0 |  cuDNN7.1.4可行  | 备注:7.0.4/ 7.0.5/ 7.1.2不明确 tensorflow-gpu v1. ...

  8. Python内置函数(55)——round

    英文文档: round(number[, ndigits]) Return the floating point value number rounded to ndigits digits afte ...

  9. Python内置函数(19)——eval

    英文文档: eval(expression, globals=None, locals=None) The arguments are a string and optional globals an ...

  10. 5.Django cookie

    概述 1.获取cookie request.COOKIES['key'] request.COOKIES.get('key') request.get_signed_cookie(key, defau ...