Zhongchen Zhou's Dynamics CRM Tips, Tricks and Portal Development Blog contains tips and tricks I discovered during my work which I want to share with other CRM Administrators and Developers. It also contains article series introducing how to use assemblies (microsoft.xrm.client.dll & microsoft.xrm.portal.dll) and portalbase solution to help creating portal Web pages that interact with Microsoft Dynamics CRM.
Sometimes it is required to check the previous value in a workflow triggered when record is updated.
The out of the box workflow create user interface only provide access to current value of the record which is the value after modification.
We could write one custom workflow activity to retrieve that value from IExecutionContext, which can be accessed from IWorkflowContext within custom workflow activity.
protected override void Execute(CodeActivityContext executionContext) { IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>(); Entity preImageEntity = context.PreEntityImages.Values.FirstOrDefault(); }
When use InOutArgument<EntityReference > in custom workflow activity, we need to specify the entity name which made it difficult to reuse, instead we could accept the attribute name of the lookup field and output entity name and record id for that lookup field. We can use entity name and record id in other workflow activity to perform generic tasks.
Here is a sample code to retrieve record id and entity name from custom workflow activity
public sealed class GetRecordIdEntityName : CodeActivity { [Input("Attribute Name for Related Record")] public InArgument<String> AttributeName { get; set; } [Output("Primary Entity Name")] public OutArgument<String> PrimaryEntityName { get; set; } [Output("Primary Record Id")] public OutArgument<String> PrimaryRecordId { get; set; } [Output("Related Entity Name")] public OutArgument<String> RelatedEntityName { get; set; } [Output("Pre Related Record Id")] public OutArgument<String> PreRelatedRecordId { get; set; } [Output("Post Related Record Id")] public OutArgument<String> PostRelatedRecordId { get; set; } /// <summary> /// Executes the workflow activity. /// </summary> /// <param name="executionContext">The execution context.</param> protected override void Execute(CodeActivityContext executionContext) { // Create the tracing service ITracingService tracingService = executionContext.GetExtension<ITracingService>(); if (tracingService == null) { throw new InvalidPluginExecutionException("Failed to retrieve tracing service."); } tracingService.Trace("Entered GetRecordId.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}", executionContext.ActivityInstanceId, executionContext.WorkflowInstanceId); // Create the context IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>(); if (context == null) { throw new InvalidPluginExecutionException("Failed to retrieve workflow context."); } tracingService.Trace("GetRecordId.Execute(), Correlation Id: {0}, Initiating User: {1}", context.CorrelationId, context.InitiatingUserId); IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>(); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); try { // TODO: Implement your custom Workflow business logic. ExecuteCore(executionContext, context, service); } catch (FaultException<OrganizationServiceFault> e) { tracingService.Trace("Exception: {0}", e.ToString()); // Handle the exception. throw; } tracingService.Trace("Exiting GetRecordId.Execute(), Correlation Id: {0}", context.CorrelationId); } private void ExecuteCore(CodeActivityContext executionContext, IWorkflowContext context, IOrganizationService service) { this.PrimaryRecordId.Set(executionContext, context.PrimaryEntityId.ToString()); this.PrimaryEntityName.Set(executionContext, context.PrimaryEntityName.ToLowerInvariant()); string attributeName = this.AttributeName.Get(executionContext); this.RelatedEntityName.Set(executionContext, null); if (!string.IsNullOrWhiteSpace(attributeName)) { Entity preImageEntity = context.PreEntityImages.Values.FirstOrDefault(); if (preImageEntity != null && preImageEntity.Contains(attributeName) && preImageEntity[attributeName] is EntityReference) { EntityReference entityRef = preImageEntity.GetAttributeValue<EntityReference>(attributeName); this.RelatedEntityName.Set(executionContext, entityRef.LogicalName.ToLowerInvariant()); this.PreRelatedRecordId.Set(executionContext, entityRef.Id.ToString()); } else { this.PreRelatedRecordId.Set(executionContext, null); } Entity postImageEntity = context.PostEntityImages.Values.FirstOrDefault(); if (postImageEntity != null && postImageEntity.Contains(attributeName) && postImageEntity[attributeName] is EntityReference) { EntityReference entityRef = postImageEntity.GetAttributeValue<EntityReference>(attributeName); this.RelatedEntityName.Set(executionContext, entityRef.LogicalName.ToLowerInvariant()); this.PostRelatedRecordId.Set(executionContext, entityRef.Id.ToString()); } else { this.PostRelatedRecordId.Set(executionContext, null); } } else { this.PreRelatedRecordId.Set(executionContext, null); this.PostRelatedRecordId.Set(executionContext, null); } } }