Using Microsoft Multiselect Lookup in your Model Driven Apps

Using Microsoft Multiselect Lookup in your Model Driven Apps

Earlier last year (September timeframe), Microsoft released a Multiselect Lookup control (similar to the Activity Party control) as part of an update to Field Services. The control does not seem to be available across the entire spectrum yet, but if you have a Field Services License or an Enterprise license, you should be able to use this control across your Dataverse environment.

In this post, I will walk through adding the control to the form, saving the record and seeing how we can use some basic plugin logic and configuration to write that data in related entities.

The control is not linked to any of the related entities that you are adding, but only stores a JSON string value to the attribute.

Let’s first go ahead and find the control. If you navigate to you Default solution, and filter by custom controls and do a search for Lookup, you will find the following controls:

Multiselect Lookup - Custom Control in Maker Portal

Next we are going to need to create an attribute (column) in the Maker Portal to host the control. Since the data in the control that is being used is in JSON format, you need to consider the size of the column that you are creating. The format that is being stored in the control contains the Id, the name and the Entity Type Name.

The image below contains two fields that are being used for this control. The first will host the PCF control, while the second will host just the text values of the control in order to be displayed in a view for the users.

Multiselect Lookup - Custom Attributes for configuration

For the purpose of the demo the Contacts column is set to a length of 1000, and the Contacts Value is set to a length of 200.

Now let’s go ahead and add the control to the form. The control is not available in the maker portal, so we will have to use the Classic/Legacy portal to add the control to the form. In the image below we see that the control has been added to the form. We will now go to the properties of the control, and select the Controls tab.

In the controls tab, we will click on the link to Add Control, and then select the control that is called MultiselectLookupControl. We then set the Web, Phone and Tablet options to use the new control instead of the Text Box (default) control.

There are two properties that need to be set in order to use the control. Those are the entity name, which is called entityName_Display_Key and the default view id, called defaultViewId_Display_Key, which can be retrieved from the Home Page views of the Contact table or from the Maker Portal when editing a view. These are displayed as part of the Urls.

Multiselect Lookup - Custom Control Form Configuration

Now that we have added the control, we can test how the control works on the form. Note that we have not implemented any logic for what we want to do once the user adds values to the control.

The images below shows you how the control will look on the form once multiple values have been added, and the data that has been saved.

Multiselect Lookup - Custom Control Form Presentation

Multiselect Lookup - Custom Control View Presentation

Now that we have seen the controls on the form, let’s start and building some of the logic that is associated with it. As this control that we are going to be using will be for a native many to many (N:N) relationship, we will need to provide some configuration data that will be stored in the plugin. Since you can have multiple controls on the form itself, the configuration will allow the plugin to loop through all the controls and process them accordingly.

The configuration information will store the column name of the control, the type of relationship (in this case only native, but in future posts I will show how to do the same for a manual relationship), and the relationship name. I also have the text attribute name if we want to use a separate attribute for storing the names only of the selected records (this is for display in views).

The configuration contains a JSON array with the following key/value pairs: Attribute Name, Text Attribute Name, Relationship Type and Relationship Name as shown below:

[{“_attributeName”:”crde5_contacts”,”_textAttributeName”:”crde5_contactvalues”,”_relationshipType”:”native”,”_relationshipName”:”crde5_accounts_contacts”}]

We will store that in the Unsecure Configuration or Secure Configuration of the Plugin Step. The image below shows you how this looks within the Plugin step, but this will have to wait till the plugin is complete. The highlighted items shows the required configuration.

Multiselect Lookup - Plugin Step Unsecured Configuration

Next, let’s go ahead and build the plugin. We add two files to the plugin.

The first files contains two classes which are used for the serialization/deserialization of the JSON string from the PCF control and the JSON string for the Unsecured configuration. The LookupObject class contains the id, name and entity name that are saved by the PCF control, as shown in the code below.

    [DataContract]
    public class LookupObject
    {
        [DataMember] 
        public string _id { get; set; }
        [DataMember] 
        public string _name { get; set; }
        [DataMember]
        public string _etn { get; set; }

        public LookupObject(string id, string name, string etn)
        {
            _id = id;
            _name = name;
            _etn = etn;
        }
    }

The second class, LookupAttribute, contains the information about each attribute that is configured for use by the plugin. We use the attribute name, text attribute name, relationship type and relationship name.

The attribute name is the name of the attribute of the PCF control field. The text attribute name is the attribute of the text only concatenation of the values from the pcf control in a separate text attribute. The relationship type will contain the value of native or manual based on the type of relationship, and the relationship name contains the name of the relationship that will be used for the Associate Requests. The code below shows the Lookup Attribute class that will be used for serialization.

    [DataContract]
    public class LookupAttribute
    {
        [DataMember] 
        public string _attributeName { get; set; }
        [DataMember]
        public string _textAttributeName { get; set; }
        [DataMember] 
        public string _relationshipType { get; set; }
        [DataMember] 
        public string _relationshipName { get; set; }

        public LookupAttribute(string attributeName, string textAttributeName, string relationshipType, string relationshipName)
        {
            _attributeName = attributeName;
            _textAttributeName = textAttributeName;
            _relationshipType = relationshipType;
            _relationshipName = relationshipName;

        }

    }

Next, let’s look at the plugin code. The plugin contains a single class (so far) that is used for the retrieval of the secure and unsecure configuration strings and for the execution of the plugin code.

In this case we will be using the unsecure configuration. The code below shows the code to get the secure and unsecure configuration and store them in variables.

        string _unsecureConfigData = string.Empty;
        string _secureConfigData = string.Empty;

        public MultiSelectLookupPostCreate(string unsecureConfig, string secureConfig)
        {
            if (!string.IsNullOrEmpty(secureConfig))
                _secureConfigData = secureConfig;

            if (!string.IsNullOrEmpty(unsecureConfig))
                _unsecureConfigData = unsecureConfig;            
        }

Next we will look at the code of the Execute function. The entire code will be shared in my github repo, so I will only be going through the relevant code.

First we will create a List of type LookupAttribute which will contain all of the different attributes that this code will need to run for. If there are multiple PCF controls, the same code can be run for all of them. The code below shows how to ready the configuration data from the JSON unsecured configuration string that was initialized previously.

List lookupAttributes;
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(_unsecureConfigData)))
{
   DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(List));
   lookupAttributes = (List)deserializer.ReadObject(stream);
}

Next, we will loop through each of the controls, and within each of the controls we will get the JSON data of that control, serialize it.

foreach (LookupAttribute attribute in lookupAttributes)
{
   string controlData = target.GetAttributeValue<string>(attribute._attributeName);
   using (MemoryStream dataStream = new MemoryStream(Encoding.Unicode.GetBytes(controlData)))
   {
      DataContractJsonSerializer dataDeserializer = new DataContractJsonSerializer(typeof(List));
      List lookupObjects = (List)dataDeserializer.ReadObject(dataStream);
...
   }
}

We will loop through each of the selected values in the PCF and add them to the entity reference collection so that it can be associated. The code below shows the loop.

List<string> lookupObjectNames = new List<string>();
EntityReferenceCollection relatedReferences = new EntityReferenceCollection();

foreach (LookupObject lookupObject in lookupObjects)
{
   Guid lookupObjectId = new Guid(lookupObject._id);
   relatedReferences.Add(new EntityReference(lookupObject._etn, lookupObjectId));
                                        
   lookupObjectNames.Add(lookupObject._name);
}

Finally, now that we have the collection, we will execute the Associate Request to add all of the values to the native relationship:

AssociateRequest request = new AssociateRequest();
request.Target = target.ToEntityReference();
request.Relationship = new Relationship(attribute._relationshipName);
request.RelatedEntities = relatedReferences;
AssociateResponse response = (AssociateResponse)service.Execute(request);

We can then if necessary, update the created record with the text attribute of the name values of the PCF control, shown below.

Entity update = new Entity(target.LogicalName);
update.Id = target.Id;
update.Attributes[attribute._textAttributeName] = String.Join(",", lookupObjectNames);
service.Update(update);

This is basically it. In the next blog articles, I will demonstrate how to update existing controls as well as how to create and update 1:n relationship records. A video will be coming shortly to demonstrate this functionality.

You can find the code in the following my github repository below

https://github.com/ariclevin/PowerPlatform/tree/master/PCF/MultiSelectLookup