Publishing Plugin Steps from Code

Publishing Plugin Steps from Code

Sometimes you write applications that contains some logic, that require you to create steps for your plugin based on other entities. For example, if you have an AutoNumber solution, you can create a plugin step for a particular entity every time you create a new AutoNumber record.

This process will allow your AutoNumber functionality to be immediately available when the user creates a new AutoNumber Settings record.

In order to do this, you will need to either retrieve or hard code the name of your assembly and plugin type as shown below.

const string ASSEMBLY_NAME = "Dyn365.Xrm.Plugins.AutoNumber";
const string PLUGIN_TYPE_NAME = "Dyn365.Xrm.Plugins.AutoNumber.PluginEntryPoint";

We then need to get the Guid of the message that we are adding, as well as get the Object Type Code and check if this message already exists, so that we bypass this logic if there is already a step for this message.

public void PublishSDKMessageProcessingStep(string entityName, Guid impersonatingUserId)
{
   Guid sdkMessageId = GetSdkMessageId("Create");
   int? objectTypeCode =  RetrieveEntityMetadataObjectTypeCode(entityName.ToLower());
   if (objectTypeCode.HasValue)
   {
      bool stepExists = RetrieveSdkMessageProcessingStep(sdkMessageId, objectTypeCode.Value);
      if (!stepExists)
         CreateSdkMessageProcessingStep(string.Format("{0}: Create of {1}", PLUGIN_TYPE_NAME, entityName), entityName.ToLower(), "", StepMode.Sync, 10, PluginStage.PostOperation, StepInvocationSource.Parent, impersonatingUserId);
   }
}

private int? RetrieveEntityMetadataObjectTypeCode(string entityName)
{
    RetrieveEntityRequest request = new RetrieveEntityRequest()
    {
        EntityFilters = EntityFilters.Entity,
        LogicalName = entityName
    };

    RetrieveEntityResponse response = (RetrieveEntityResponse)service.Execute(request);
    int? result = response.EntityMetadata.ObjectTypeCode;
    return result;
}

The RetrieveSdkMessageProcessingStep is a queryexpression on the SdkMessageProcessingStep with multiple linked entities (SdkMessageFilter and PluginType) to check if the record already exists. It is probably easiest to do this as a FetchXml query, but works both ways. The CreateSdkMessageProcessingStep function was called after the retrieval of the required values. This method will create the SDKMessageProcessingStep record. We will need to get the Ids of some of the Plugin types in order to complete the save operation

private Guid CreateSdkMessageProcessingStep(string name, string entityName, string configuration, StepMode mode, int rank, PluginStage stage, StepInvocationSource invocationSource, Guid impersonatingUserId)
{
    Entity step = new Entity("sdkmessageprocessingstep");
    step["name"] = name;
    step["description"] = name;
    step["configuration"] = configuration;
    step["mode"] = new OptionSetValue(mode.ToInt());
    step["rank"] = rank;
    step["stage"] = new OptionSetValue(stage.ToInt());
    step["supporteddeployment"] = new OptionSetValue(0); // Server Only
    step["invocationsource"] = new OptionSetValue(invocationSource.ToInt());

    Guid sdkMessageId = GetSdkMessageId("Create");
    Guid sdkMessageFilterId = GetSdkMessageFilterId(entityName, sdkMessageId);

    Guid assemblyId = GetPluginAssemblyId(ASSEMBLY_NAME);
    Guid pluginTypeId = GetPluginTypeId(assemblyId, PLUGIN_TYPE_NAME);

    step["plugintypeid"] = new EntityReference("plugintype", pluginTypeId);
    step["sdkmessageid"] = new EntityReference("sdkmessage", sdkMessageId);
    step["sdkmessagefilterid"] = new EntityReference("sdkmessagefilter", sdkMessageFilterId);

    if (impersonatingUserId != Guid.Empty)
        step["impersonatinguserid"] = new EntityReference("systemuser", impersonatingUserId);

    try
    {
        Guid stepId = service.Create(step);
        return stepId;
    }
    catch (InvalidPluginExecutionException invalidPluginExecutionException)
    {
        throw invalidPluginExecutionException;
    }
    catch (Exception exception)
    {
        throw exception;
    }
}

private Guid GetSdkMessageId(string SdkMessageName)
{
    try
    {
        //GET SDK MESSAGE QUERY
        QueryExpression sdkMessageQueryExpression = new QueryExpression("sdkmessage");
        sdkMessageQueryExpression.ColumnSet = new ColumnSet("sdkmessageid");
        sdkMessageQueryExpression.Criteria = new FilterExpression
        {
            Conditions =
                {
                    new ConditionExpression
                    {
                        AttributeName = "name",
                        Operator = ConditionOperator.Equal,
                        Values = {SdkMessageName}
                    },
                }
        };

        //RETRIEVE SDK MESSAGE
        EntityCollection sdkMessages = service.RetrieveMultiple(sdkMessageQueryExpression);
        if (sdkMessages.Entities.Count != 0)
        {
            return sdkMessages.Entities.First().Id;
        }
        throw new Exception(String.Format("SDK MessageName {0} was not found.", SdkMessageName));
    }
    catch (InvalidPluginExecutionException invalidPluginExecutionException)
    {
        throw invalidPluginExecutionException;
    }
    catch (Exception exception)
    {
        throw exception;
    }
}

private Guid GetSdkMessageFilterId(string EntityLogicalName, Guid sdkMessageId)
{
    try
    {
        //GET SDK MESSAGE FILTER QUERY
        QueryExpression sdkMessageFilterQueryExpression = new QueryExpression("sdkmessagefilter");
        sdkMessageFilterQueryExpression.ColumnSet = new ColumnSet("sdkmessagefilterid");
        sdkMessageFilterQueryExpression.Criteria = new FilterExpression
        {
            Conditions =
                {
                    new ConditionExpression
                    {
                        AttributeName = "primaryobjecttypecode",
                        Operator = ConditionOperator.Equal,
                        Values = {EntityLogicalName}
                    },
                    new ConditionExpression
                    {
                        AttributeName = "sdkmessageid",
                        Operator = ConditionOperator.Equal,
                        Values = {sdkMessageId}
                    },
                }
        };

        //RETRIEVE SDK MESSAGE FILTER
        EntityCollection sdkMessageFilters = service.RetrieveMultiple(sdkMessageFilterQueryExpression);

        if (sdkMessageFilters.Entities.Count != 0)
        {
            return sdkMessageFilters.Entities.First().Id;
        }
        throw new Exception(String.Format("SDK Message Filter for {0} was not found.", EntityLogicalName));
    }
    catch (InvalidPluginExecutionException invalidPluginExecutionException)
    {
        throw invalidPluginExecutionException;
    }
    catch (Exception exception)
    {
        throw exception;
    }
}

private Guid GetPluginAssemblyId(string assemblyName)
{
    try
    {
        //GET ASSEMBLY QUERY
        QueryExpression pluginAssemblyQueryExpression = new QueryExpression("pluginassembly");
        pluginAssemblyQueryExpression.ColumnSet = new ColumnSet("pluginassemblyid");
        pluginAssemblyQueryExpression.Criteria = new FilterExpression
        {
            Conditions =
                {
                    new ConditionExpression
                    {
                        AttributeName = "name",
                        Operator = ConditionOperator.Equal,
                        Values = {assemblyName}
                    },
                }
        };

        //RETRIEVE ASSEMBLY
        EntityCollection pluginAssemblies = service.RetrieveMultiple(pluginAssemblyQueryExpression);
        Guid assemblyId = Guid.Empty;
        if (pluginAssemblies.Entities.Count != 0)
        {
            //ASSIGN ASSEMBLY ID TO VARIABLE
            assemblyId = pluginAssemblies.Entities.First().Id;
        }
        return assemblyId;
    }
    catch (InvalidPluginExecutionException invalidPluginExecutionException)
    {
        throw invalidPluginExecutionException;
    }
    catch (Exception exception)
    {
        throw exception;
    }
}

private Guid GetPluginTypeId(Guid assemblyId, string PluginTypeName)
{
    try
    {
            //GET PLUGIN TYPES WITHIN ASSEMBLY
            QueryExpression pluginTypeQueryExpression = new QueryExpression("plugintype");
            pluginTypeQueryExpression.ColumnSet = new ColumnSet("plugintypeid");
            pluginTypeQueryExpression.Criteria = new FilterExpression
            {
                Conditions =
                {
                    new ConditionExpression
                    {
                        AttributeName = "pluginassemblyid",
                        Operator = ConditionOperator.Equal,
                        Values = {assemblyId}
                    },
                    new ConditionExpression
                    {
                        AttributeName = "typename",
                        Operator = ConditionOperator.Equal,
                        Values = {PluginTypeName}
                    },
                }
            };

            //RETRIEVE PLUGIN TYPES IN ASSEMBLY
            EntityCollection pluginTypes = service.RetrieveMultiple(pluginTypeQueryExpression);

            //RETURN PLUGIN TYPE ID
            if (pluginTypes.Entities.Count != 0)
            {
                return pluginTypes.Entities.First().Id;
            }
            throw new Exception(String.Format("Plugin Type {0} was not found in Assembly", PluginTypeName));
    }
    catch (InvalidPluginExecutionException invalidPluginExecutionException)
    {
        throw invalidPluginExecutionException;
    }
    catch (Exception exception)
    {
        throw exception;
    }
}

As you can see, the process is pretty simple, but requires a lot of trips to get the unique identifier values of the Assembly, Plugin Type, Plugin and Message Id, and Message Filter Id.