關注本人微信和易信公衆號: 微軟動態CRM專家羅勇 ,回覆244或者20170306可方便獲取本文,同時能夠在第一間獲得我發佈的最新的博文信息,follow me!個人網站是 www.luoyong.me 。微信
爲了方便說明,我首先建立一個自定義工做流活動,使用的代碼以下。ide
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,注意註冊以前,你的程序集要簽名哦。ui
註冊以下,Dynamics 365 Online不支持註冊到None中,因此Step 3要選擇 Sandbox。可是個人代碼中使用了反射來讀取參數值,使用的也是本地部署的Dynamics 365,因此註冊到None中,註冊到Sandbox中的程序集具備的權限要小得多。點擊下面的Register Selected Plugins 按鈕。電腦分辨率低的童鞋注意,可能要用tab鍵跳轉到這個按鈕。spa
一下子告訴我註冊成功。插件
根據須要,最好是調整下這個自定義工做流活動的WorkflowActivityGroupName和Name,我這裏調整以下。而後點擊保存按鈕保存就能夠了。這個保存按鈕好小啊。日誌
而後咱們在工做流中就可使用了,我這裏作一個工做流以下,監控的是單行文本字段的變動。我這也也勾選了 做爲按需工做流,因此是能夠手工啓動運行的。blog
固然須要激活工做流,由於我要看的東西都是寫在工做流的trace裏面,因此我還去 設置 > 管理 > 系統設置 > 自定義裏面,將啓用插件跟蹤日誌的日誌記錄這個選項設置爲全部。ci
變動字段值以後咱們去看插件日誌,在設置>自定義>插件跟蹤日誌裏面查看。不過還真有時候看不到,通常是由於你註冊在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.WorkflowOperationInProgre***esult(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.WorkflowOperationInProgre***esult(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); 這樣容易避免由於觸發該工做流的用戶權限不夠而帶來工做流運行失敗的問題。