关注本人微信和易信公众号: 微软动态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. SUSE12Sp3安装配置.net core 生产环境-总汇(持续更新中...)

    最近正在使用SUSE系统,项目环境是没有外网的,所以提供的基本都是离线安装,对应的安装包可能需要自行去下载,我这边就不整理了. 网上查找SUSE的资料比较少,于是整理了一下,希望对有需要的人有一点点帮 ...

  2. [Swift]LeetCode965. 单值二叉树 | Univalued Binary Tree

    A binary tree is univalued if every node in the tree has the same value. Return true if and only if ...

  3. MySql和Oracle数据库区别

    Oracle与mysql区别: 1.Oracle有表空间,mysql没有表空间. 2.mysql的char类型取值范围0-255字节,varchar为0-65535字节 3.oracle的char类型 ...

  4. Android device debug (adb) by Charge Only mode

    Android device debug by Charge Only mode Method 1 Connect devices to computer and execute lsusb Find ...

  5. C/C++数据在内存中的存储方式

    目录 1 内存地址 2 内存空间   在学习C/C++编程语言时,免不了和内存打交道,在计算机中,我们存储有电影,文档,音乐等数据,这些数据在内存中是以什么形式存储的呢?下面做一下简单介绍. 本文是学 ...

  6. Unable to preventDefault inside passive event listener due to target being treated as passive

    Unable to preventDefault inside passive event listener due to target being treated as passive 今天在做项目 ...

  7. 细说javascripe事件传播流程

    当我们使用js时,经常会遇到事件传播流程的问题,下面我说一下我的观点. 在js触发某个事件时会相应生成一个事件对象,而这个事件对象则会根据DOM事件流的方向进传递,而传递的顺序如下图所示: 事件对象会 ...

  8. 通过LRU实现通用高效的超时连接探测

    编写网络通讯都要面对一个问题,就是要把很久不存活的死连接清除,如果不这样做那死连接最终会占用大量内存影响服务运作!在实现过程中一般都会使用ping,pong原理,通过ping,pong来更新连接的时效 ...

  9. 爬虫入门(四)——Scrapy框架入门:使用Scrapy框架爬取全书网小说数据

    为了入门scrapy框架,昨天写了一个爬取静态小说网站的小程序 下面我们尝试爬取全书网中网游动漫类小说的书籍信息. 一.准备阶段 明确一下爬虫页面分析的思路: 对于书籍列表页:我们需要知道打开单本书籍 ...

  10. Spring 完美配置跨域请求

    在SpringBoot2.0 上的跨域 用以下代码配置 即可完美解决你的前后端跨域请求问题 import org.springframework.context.annotation.Bean; im ...