关注本人微信和易信公众号: 微软动态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. c++ 之bind使用

    网络编程中, 经常要使用到回调函数. 当底层的网络框架有数据过来时,往往通过回调函数来通知业务层. 这样可以使网络层只专注于 数据的收发, 而不必关心业务 在c语言中, 回调函数的实现往往通过函数指针 ...

  2. VSCode调试Flutter的问题解决

    错误:Target of URI doesn't exist: package:fultter/material.dart 原因:flutter版本不匹配.flutter升级到了最新版本,而项目引用的 ...

  3. IOS微信点击input弹出输入法,关闭后页面留白解决方案

    场景:IOS用微信点击input框弹出输入法后 不管你是输入信息,还是不输入直接点完成关闭输入法,都会导致页面被挤上去后产生留白,从而改变页面布局             解决方法: 给input添加 ...

  4. [Swift]LeetCode126. 单词接龙 II | Word Ladder II

    Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformat ...

  5. [Swift]LeetCode763. 划分字母区间 | Partition Labels

    A string S of lowercase letters is given. We want to partition this string into as many parts as pos ...

  6. javascript 使用小技巧总结

    按位取反 ~a 即:返回 -(a+1),会去掉小数点. let a = 3.14; let b = ~a; //b = -(3.14+1) 取整 为-4: let c = ~b; //c = -(-4 ...

  7. WebView 讲义

    http://reezy.me/p/20170515/android-webview/  (比较全面) 参考 https://developer.android.com/reference/andro ...

  8. 死磕 java集合之WeakHashMap源码分析

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 WeakHashMap是一种弱引用map,内部的key会存储为弱引用,当jvm gc的时 ...

  9. IdentityServer4之Implicit(隐式许可)

    IdentityServer4之Implicit(隐式许可) 参考 官方文档:3_interactive_login .7_javascript_client 概念:隐式许可 认证服务端配置 认证服务 ...

  10. C++版 - 剑指offer之面试题37:两个链表的第一个公共结点[LeetCode 160] 解题报告

    剑指offer之面试题37 两个链表的第一个公共结点 提交网址: http://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?t ...