Monday, October 29, 2018

Dynamics CRM - Auto generate plugin steps.

I believe that you are experienced with the plugin registration and adding steps using plugin registration tool. But sometimes you may need to add/remove plugin steps programmatically.

Adding a plugin step means, creating a SdkMessageProcessingStep record in CRM.

Before creating a SdkMessageProcessingStep record you need to retrieve following.
  1. The message id.
    /// <summary>
    /// Gets the message identifier.
    /// </summary>
    /// <param name="context">The context.</param>
    /// <param name="sdkMessageName">Name of the SDK message.</param>
    /// <returns>
    /// The message id.
    /// </returns>
    /// <exception cref="InvalidPluginExecutionException">No sdk messages found with the sdk message name.</exception>
    private static Guid GetMessageId(ServiceContext context, string sdkMessageName)
    {
        using (context)
        {
            // Gets the sdk message
            var sdkMessage = context.SdkMessageSet.Where(s => s.Name == sdkMessageName).FirstOrDefault();
                    
            // Throws an error if no sdk messages found.
            if (sdkMessage == null)
            {
                throw new InvalidPluginExecutionException(string.Format("No sdk messages found with the sdk message name '{0}'.", sdkMessageName));
            }
    
            return sdkMessage.Id;
        }
    }
  2.  The plugin type id.
    /// <summary>
    /// Gets the plugin type identifier.
    /// </summary>
    /// <param name="context">The context.</param>
    /// <param name="assemblyName">Name of the assembly.</param>
    /// <param name="pluginTypeName">Name of the plugin type.</param>
    /// <returns>
    /// The plugin type id.
    /// </returns>
    /// <exception cref="InvalidPluginExecutionException">No plugin assemblies found with the assmbly name
    /// or
    /// No plugin types found with the plugin type and the assmbly name</exception>
    private static Guid GetPluginTypeId(ServiceContext context, string assemblyName, string pluginTypeName)
    {
        using (context)
        {
            // Gets the plugin assembly.
            var pluginAssembly = context.PluginAssemblySet.Where(p => p.Name == assemblyName).FirstOrDefault();
                    
            // Throws an error if no plugin assembly found with the name.
            if (pluginAssembly == null)
            {
                throw new InvalidPluginExecutionException(string.Format("No plugin assemblies found with the assmbly name '{0}'", assemblyName));
            }
    
            var assemblyId = pluginAssembly.Id;
                    
            // Gets the plugin type
            var pluginType = context.PluginTypeSet.Where(p => p.PluginAssemblyId.Id == assemblyId && p.TypeName == pluginTypeName).FirstOrDefault();
                    
            // Throws an error if no plugin types found.
            if (pluginType == null)
            {
                throw new InvalidPluginExecutionException(string.Format("No plugin types found with the plugin type '{0}' and the assmbly name '{1}'", pluginType.ToString(), assemblyName));
            }
    
            return pluginType.Id;
        }
    }
  3. The sdk message filter id.
    /// <summary>
    /// Gets the SDK message filter identifier.
    /// </summary>
    /// <param name="context">The context.</param>
    /// <param name="entityName">Name of the entity.</param>
    /// <returns>
    /// The message filter id.
    /// </returns>
    /// <exception cref="InvalidPluginExecutionException">No sdk message filters found for the entity.</exception>
    private static Guid GetSdkMessageFilterId(ServiceContext context, string entityName)
    {
        using (context)
        {
            // Gets the message id.
            Guid messageId = GetMessageId(context, ContextMessageName.Create);
                    
            // Get the message filter.
            var sdkMessageFilter = context.SdkMessageFilterSet.Where(s => s.PrimaryObjectTypeCode == entityName && s.SdkMessageId.Id == messageId).FirstOrDefault();
                    
            // Throws an error if no message filters found.
            if (sdkMessageFilter == null)
            {
                throw new InvalidPluginExecutionException( string.Format("No sdk message filters found for the entity '{0}'", entityName));
            }
    
            return sdkMessageFilter.Id;
        }
    }

Now we have the message id,  plugin type id and the sdk message filter id.

We can define the assembly name and the plugin type name as properties.
/// <summary>
/// The assembly name
/// </summary>
private static string AssemblyName = "PluginSample.Plugins";

/// <summary>
/// The plugin type name
/// </summary>
private static string PluginTypeName = "PluginSample.Plugins.TestHandler";


With all the above things in hand, now following method can be used to add plugin steps programatically.
/// <summary>
/// Adds the plugin step.
/// </summary>
/// <param name="service">The service.</param>
/// <param name="entityLogicalName">The entity logical name.</param>
/// <param name="contextMessageName">Name of the context message.("Create", "Update", "Delete", "Associate", "Disassociate")</param>
public static void AddPluginStep(IOrganizationService service, string entityLogicalName, string contextMessageName)
{ 
    using (var context = new ServiceContext(service))
    {
        var stepName = string.Format("PluginSample.Plugins.TestHandler: {0} of {1}", contextMessageName, entityLogicalName);

        // Gets the message name.
        var messageId = GetMessageId(context, contextMessageName);

        // Gets the plugin type id.
        var pluginTypeId = GetPluginTypeId(context, AssemblyName, PluginTypeName);

        // Gets the sdk message filter id.
        var sdkMessageFilterId = GetSdkMessageFilterId(context, entityLogicalName);

        // Check the plugin step is already regiseterd and if not register the new step.
        var sdkMessageProcessingStep = context.SdkMessageProcessingStepSet.Where(s => s.SdkMessageFilterId.Id == sdkMessageFilterId && s.EventHandler.Id == pluginTypeId).FirstOrDefault();
        if (sdkMessageProcessingStep == null || sdkMessageProcessingStep.Id == Guid.Empty)
        {
            // Creates the sdk message processing step.
            SdkMessageProcessingStep step = new SdkMessageProcessingStep
            {
                AsyncAutoDelete = false,
                Mode = new OptionSetValue((int)SdkMessageProcessingStepMode.Synchronous),
                Name = stepName,
                EventHandler = new EntityReference(PluginType.EntityLogicalName, pluginTypeId),
                Rank = 1,
                SdkMessageId = new EntityReference(SdkMessage.EntityLogicalName, messageId),
                Stage = new OptionSetValue((int)PipelineStage.PreOperation),
                SupportedDeployment = new OptionSetValue((int)SdkMessageProcessingStepSupportedDeployment.ServerOnly),
                SdkMessageFilterId = new EntityReference(SdkMessageFilter.EntityLogicalName, sdkMessageFilterId)
            };

            service.Create(step);
        }
    }
}

In the same manner we can remove the plugin step by deleting the SdkMessageProcessingStep record.
/// <summary>
/// Removes the plugin step.
/// </summary>
/// <param name="service">The service.</param>
/// <param name="entityLogicalName">The entity logical name.</param>
public static void RemovePluginStep(IOrganizationService service, string entityLogicalName)
{
    using (var context = new ServiceContext(service))
    {
        // Gets the plugin type id.
        var pluginTypeId = GetPluginTypeId(context, AssemblyName, PluginTypeName);

        // Gets the message filter id.
        var sdkMessageFilterId = GetSdkMessageFilterId(context, entityLogicalName);

        // Filter the message processing step.
        var sdkMessageProcessingStep = context.SdkMessageProcessingStepSet.Where(s => s.SdkMessageFilterId.Id == sdkMessageFilterId && s.EventHandler.Id == pluginTypeId).FirstOrDefault();

        if (sdkMessageProcessingStep != null)
        {
            // Delete sdk message processing step.
            service.Delete(SdkMessageProcessingStep.EntityLogicalName, sdkMessageProcessingStep.Id);
        }
    }
}

In above example I'm using the early bound classes. If you need late bound code examples, please let me know in comment section.


No comments:

Post a Comment