In the first installment of this series, we laid the groundwork by creating the entity needed for our next phase: the plugin. You might be wondering why we should go the plugin route when similar results could potentially be achieved using JavaScript and a dash of creativity. Through experience, I’ve discovered that plugins offer a safer and more reliable solution for our purposes.
In this part, we’ll walk you through the steps methodically. We’ll explain the code in detail and ensure it makes sense. You’ll find the complete code on Site 2 for easy copying.
To fully grasp the code and its functionality, a foundational understanding of how plugins work and are programmed is required.
Now, let’s dive into the main content of your article:
Info from the Action
Since we use an action to call the plugin, we need to listen to the input from this action. The action itself will be covered in the final section of this series, where we connect the missing part: the button.
In our example, we need to retrieve the clone configuration from the action.
csharp
string cloneConfiguration = context.InputParameters["cloneConfiguration"] as string;
Guid sourceGuid = new Guid(context.InputParameters["guid"] as string);
As you can see, we store the GUID (Globally Unique Identifier), the ID of the data row where the cloning is initiated, as well as the clone configuration.
With the Clone Configuration from the Action, we will get the table data. So, we need to make a QueryExpression and retrieve all info from the entity.
csharp
QueryExpression queryCloneConfiguration = new QueryExpression("wwp_cloneconfiguration");
queryCloneConfiguration.ColumnSet.AddColumns("wwp_purpose", "wwp_targetentity", "wwp_sourceentity", "wwp_sourceattributes", "wwp_targetattributes");
queryCloneConfiguration.Criteria.AddCondition("wwp_purpose", ConditionOperator.Equal, cloneConfiguration);
EntityCollection resultCloneConfiguration = service.RetrieveMultiple(queryCloneConfiguration);
As you can see, we’ve structured our Query Expression by including only the necessary columns and adding a filter condition.
Typically, we use a condition to verify if any results are returned from the query.
If we receive a result, we’ll need to store the different columns in variables. Let’s proceed with that. By doing this, we ensure that the relevant data is organized and ready for further processing. This approach helps in maintaining clarity and efficiency in our code execution. Ready to dive in?
csharp
Entity cloneConfig = resultCloneConfiguration.Entities[0];
string targetEntity = cloneConfig.GetAttributeValue<string>("ppt_targetentity");
string sourceEntity = cloneConfig.GetAttributeValue<string>("ppt_sourceentity");
string sourceAttributes = cloneConfig.GetAttributeValue<string>("ppt_sourceattributes");
string targetAttributes = cloneConfig.GetAttributeValue<string>("ppt_targetattributes");
In this section of the code, we store all the information we previously set up a form for.
Now that we have both entities and both attribute lists, the next step involves splitting these lists of attributes into arrays. The reason for this is that the custom component saves the selected attributes in a text column, separated by commas. By splitting these lists into arrays, we ensure that each attribute is properly isolated and ready for further processing. Let’s get started on this!
csharp
// Split the source and target attributes into arrays and trim spaces
string[] sourceAttributesArray = sourceAttributes.Split(',').Select(attr => attr.Trim()).ToArray();
string[] targetAttributesArray = targetAttributes.Split(',').Select(attr => attr.Trim()).ToArray();
As a safety feature, I would suggest you make another condition and check if the source and target attribute lists have the same length. Otherwise, it will fail. Not could, it will.
csharp
if (sourceAttributesArray.Length == targetAttributesArray.Length) {
// Retrieve the source entity
Entity sourceEntityRecord = service.Retrieve(sourceEntity, sourceGuid, new ColumnSet(sourceAttributesArray));
// Create the target entity
Entity targetEntityRecord = new Entity(targetEntity);
// Map the source and target attributes
for (int i = 0; i < sourceAttributesArray.Length; i++) {
string sourceAttribute = sourceAttributesArray[i];
string targetAttribute = targetAttributesArray[i];
tracingService.Trace($"Mapping source attribute '{sourceAttribute}' to target attribute '{targetAttribute}'.");
if (sourceEntityRecord.Contains(sourceAttribute)) {
targetEntityRecord[targetAttribute] = sourceEntityRecord[sourceAttribute];
} else {
tracingService.Trace($"Source entity does not contain attribute '{sourceAttribute}'.");
}
}
}
If you do not have the same logical names, that step will fail.
If you have done that successfully, you can now close all branches, set traces or errors, and call it a day.
On the next page, you will see my full code. Please keep in mind that in my scenario, I deactivated the source data to avoid any conflicts in a business process. You are free to remove that.