web
You’re offline. This is a read only version of the page.
close
Skip to main content
Community site session details

Community site session details

Session Id :
Dynamics 365 Community / Blogs / Bring your code 2 life / Set Multi-OptionSet values ...

Set Multi-OptionSet values with a custom workflow activity

Andreas Cieslik Profile Picture Andreas Cieslik 9,267
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;
}
}
}

This was originally posted here.

Comments

*This post is locked for comments