关注本人微信和易信公众号: 微软动态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. [译文]Domain Driven Design Reference(六)—— 提炼战略设计

    本书是Eric Evans对他自己写的<领域驱动设计-软件核心复杂性应对之道>的一本字典式的参考书,可用于快速查找<领域驱动设计>中的诸多概念及其简明解释. 其它本系列其它文章 ...

  2. 企业IT管理员IE11升级指南【9】—— IE10与IE11的功能对比

    企业IT管理员IE11升级指南 系列: [1]—— Internet Explorer 11增强保护模式 (EPM) 介绍 [2]—— Internet Explorer 11 对Adobe Flas ...

  3. 实现CSS隐藏滚动条并可以滚动内容

    隐藏滚动条的同时还需要支持滚动,我们经常在前端开发中遇到这种情况,最容易想到的是加一个iscroll插件,但其实现在CSS也可以实现这个功能,我已经在很多地方使用了,下面一起看看这三种方法. 方法1: ...

  4. [Swift]LeetCode35. 搜索插入位置 | Search Insert Position

    Given a sorted array and a target value, return the index if the target is found. If not, return the ...

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

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

  6. [Swift]LeetCode372. 超级次方 | Super Pow

    Your task is to calculate ab mod 1337 where a is a positive integer and bis an extremely large posit ...

  7. [Swift]LeetCode980. 不同路径 III | Unique Paths III

    On a 2-dimensional grid, there are 4 types of squares: 1 represents the starting square.  There is e ...

  8. Android版本号列表

    Android版本号列表

  9. PHP分页倒序时,需要注意的问题

    PHP分页倒序请求,如果有新数据加入,下一页会出现重复数据 解决方案: 第一次查询时,给前端返回一个查询时间戳,下一次请求时,把时间戳带过来,只查询比这个时间戳小的数据

  10. 使用ML.NET预测纽约出租车费

    有了上一篇<.NET Core玩转机器学习>打基础,这一次我们以纽约出租车费的预测做为新的场景案例,来体验一下回归模型. 场景概述 我们的目标是预测纽约的出租车费,乍一看似乎仅仅取决于行程 ...