As the relatively new multi-optionset field type arrived with v.9 of Dynamics 365 the need to set values via workflow is quite a common requirement.
So I searched within the community to find some ideas on how to solve and create such a workflow activity and so I stumbled upon Demian Raschkovan’s Workflow Tools with can be found on his github repository:
It gave me some basic ideas to reach my requirements which are:
- Should be generic for any type of entity
- Ability to specify the attribute name of the required multi-optionset for that entity
- Provide a list of multi-optionset values (comma-separated)
- Keep existing values (True/Yes => add provided values / False/No => replace all values with the provided values
- Remove specific value(s) from an existing set of values
My code sample (without other dependencies):
using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Workflow; using System; using System.Activities; using Microsoft.Xrm.Sdk.Query; using My.CRM.Code.Shared; namespace My.CRM.Workflows { public class SetMultiOptionSetValues : WorkFlowActivityBase { [Input("Record URL")] [RequiredArgument] public InArgument<string> RecordUrl { get; set; } [Input("Attribute Name")] [RequiredArgument] public InArgument<string> AttributeName { get; set; } [Input("Multi-OptionSet Values (,-separated)")] [RequiredArgument] public InArgument<string> MultiOptionSetValues { get; set; } [Input("Keep Existing Values")] [Default("false")] public InArgument<bool> KeepExistingValues { get; set; } [Input("Remove Specific Values")] [Default("false")] public InArgument<bool> RemoveSpecificValues { get; set; } protected override void ExecuteActivity() { string attributeName = this.AttributeName.Get<string>(this.ExecutionContext); OptionSetValueCollection optionSetValues = GetMultiOptionSetValues(); EntityReference entityReference = GetEntityReference(); UpdateEntity(entityReference, attributeName, optionSetValues); } private OptionSetValueCollection GetMultiOptionSetValues() { var optionSetValues = this.MultiOptionSetValues.Get<string>(this.ExecutionContext); string[] optionSetValuesArray = optionSetValues.Split(','); if (optionSetValuesArray == null || optionSetValuesArray.Length == 0) { this.TraceMessage("No optionset values could be found."); return null; } else { OptionSetValueCollection osvCol = new OptionSetValueCollection(); foreach (string optionSetValueStr in optionSetValuesArray) { int value = 0; if (Int32.TryParse(optionSetValueStr, out value)) { OptionSetValue optionSetValue = new OptionSetValue(value); osvCol.Add(optionSetValue); } else throw new InvalidWorkflowException("The provided optionset value is not a valid integer '" + optionSetValueStr + "'."); } return osvCol; }; } private EntityReference GetEntityReference() { var recordUrl = this.RecordUrl.Get<string>(this.ExecutionContext); if (string.IsNullOrWhiteSpace(recordUrl)) throw new ArgumentNullException("Record URL is empty."); return new DynamicUrlParser(recordUrl).GetEntityReference(this.OrganizationService); } private void UpdateEntity(EntityReference entityReference, string attributeName, OptionSetValueCollection newOptionSetValues) { if (entityReference == null || newOptionSetValues == null || string.IsNullOrWhiteSpace(attributeName)) return; OptionSetValueCollection existingOptionSetValues = GetExistingOptionSetValues(entityReference, attributeName); Entity targetEntity = new Entity(entityReference.LogicalName, entityReference.Id); targetEntity.Attributes.Add(attributeName, MergeOptionSetCollections(newOptionSetValues, existingOptionSetValues)); this.TraceMessage("Updating the entity record..."); this.OrganizationService.Update(targetEntity); this.TraceMessage("Entity record updated successfully."); } private OptionSetValueCollection GetExistingOptionSetValues(EntityReference entityReference, string attributeName) { this.TraceMessage("Retrieving existing values..."); bool removeValues = this.RemoveSpecificValues.Get<bool>(this.ExecutionContext); bool attributeValues = this.KeepExistingValues.Get<bool>(this.ExecutionContext); if (!attributeValues && !removeValues) return null; Entity record = this.OrganizationService.Retrieve(entityReference.LogicalName, entityReference.Id, new ColumnSet(new string[] { attributeName })); this.TraceMessage("Existing values have been retrieved correctly"); if (record != null && record.Contains(attributeName)) return record[attributeName] as OptionSetValueCollection; else return null; } private OptionSetValueCollection MergeOptionSetCollections(OptionSetValueCollection newValues, OptionSetValueCollection existingValues) { this.TraceMessage("Merging new and exiting multi-select optionset values..."); if (existingValues == null && newValues == null) return new OptionSetValueCollection(); if (existingValues == null) return newValues; if (newValues == null) return existingValues; bool removeValues = this.RemoveSpecificValues.Get<bool>(this.ExecutionContext); foreach (OptionSetValue newValue in newValues) { if (!existingValues.Contains(newValue) && !removeValues) existingValues.Add(newValue); else if (existingValues.Contains(newValue) && removeValues) existingValues.Remove(newValue); } this.TraceMessage("New and exiting multi-select optionset values have been merged correctly. Total options: {0} ", existingValues.Count); return existingValues; } } }
Nice Blog, Thanks for share an amazing information dynamics 365 customer portal
ReplyDelete