Skip to main content

MSCRM Plugin Development Steps

This section will cover the following:

How to Start Developing a Plug-in

1. Download CRM SDK. This will provide you all the information, required SDK assemblies, and many helpful samples.
2. Set up your plug-in project in Visual Studio. This will be a .NET class library.
developing a plugin img 1
3. Add References. At a minimum, you will need Microsoft.Xrm.Sdk, obtained from CRM SDK.
4. Extend the class from Microsoft.Xrm.Sdk.IPlugin
5. Write your code
Developing a CRM Plug-in
6. At the project, sign the assembly. This is required in order to be able to deploy the plugin.
developing a plugin img 3
7. Compile the assembly and deploy using Plugin Registration Tool.

Example 1: Update parent record based on multiple fields

Here’s an extremely simplified example of a plug-in. We have parent record with a field for total and a single child with unit and rate. When either unit or rate changes, we want to multiply these and update to the total of the parent.
Design Notes
  • Because a user may only update units and not update rate, we cannot use a target which would only include the attributes that were actually updated. Therefore, we use an image to make sure we get the necessary attributes, without having to do additional data retrieve.
  • The plug-in is post-update, so we will have access to post-image, showing all values like they are after an update.
  • When registering the plugin, we set filtering attributes to include only rate and units. Therefore, the plugin will not trigger needlessly if something unrelated updates.
  • When setting up the Image, we only need rate, units, and parent ID.
Plug-in Code
using System;
using Microsoft.Xrm.Sdk;

namespace CRMBook
{
    public class Update : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {

            // Obtain the execution context from the service provider.
            IPluginExecutionContext context =
                (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            // Get a reference to the Organization service.
            IOrganizationServiceFactory factory =
                (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);

            if (context.InputParameters != null)
            {
                //entity = (Entity)context.InputParameters["Target"];
                //Instead of getting entity from Target, we use the Image
                Entity entity = context.PostEntityImages["PostImage"];

                Money rate = (Money)entity.Attributes["po_rate"];
                int units = (int)entity.Attributes["po_units"];
                EntityReference parent = (EntityReference)entity.Attributes["po_parentid"];

                //Multiply
                Money total = new Money(rate.Value * units);

                //Set the update entity
                Entity parententity = new Entity("po_parententity");
                parententity.Id = parent.Id;
                parententity.Attributes["po_total"] = total;

                //Update
                service.Update(parententity);

            }

        }

    }
}

Example 2: Update same record based on multiple fields

This example is similar to the one above, but let’s assume our total is on same entity.
  • In order to change the total on the fly, the plug-in needs to be pre-update
  • This means we do not have access to post the image and have to use the pre-image instead. Therefore, we need to get all the changed values from the target, and unchanged values from the image.
Plug-in Code
if (context.InputParameters != null)
            {
                //Get Target - includes everything changed
                Entity entity = (Entity)context.InputParameters["Target"];

                //Get Pre Image
                Entity image = context.PreEntityImages["PreImage"];

                //If value was changed, get it from target.
                //Else, get the value from preImage
                Money rate = null;
                if(entity.Attributes.Contains("po_rate"))
                    rate = (Money)entity.Attributes["po_rate"];
                else
                    rate =  (Money)image.Attributes["po_rate"];
                int units = 0;
                if (entity.Attributes.Contains("po_units"))
                    units = (int)entity.Attributes["po_units"];
                else
                    units = (int)image.Attributes["po_units"];

                //Multiply
                Money total = new Money(rate.Value * units);

                //Set the value to target
                entity.Attributes.Add("po_total",total);

                //No need to issue additional update
            }
 
A plug-in is a custom business logic that integrates with Microsoft Dynamics CRM to modify or extend the standard behavior of the platform. Plug-ins act as event handlers and are registered to execute on a particular event in CRM. Plugins are written in either C# or VB and can run either in synchronous or asynchronous mode.
Some scenarios where you would write a plugin are −
  • You want to execute some business logic such as updating certain fields of a record or updating related records, etc. when you create or update a CRM record.
  • You want to call an external web service on certain events such as saving or updating a record.
  • You want to dynamically calculate the field values when any record is opened.
  • You want to automate processes such as sending e-mails to your customers on certain events in CRM.

Event Framework

The Event Processing Framework in CRM processes the synchronous and asynchronous plugin requests by passing it to the event execution pipeline. Whenever an event triggers a plugin logic, a message is sent to the CRM Organization Web Service where it can be read or modified by other plugins or any core operations of the platform.

Plugin Pipeline Stages

The entire plugin pipeline is divided in multiple stages on which you can register your custom business logic. The pipeline stage specified indicates at which stage of the plugin execution cycle, your plugin code runs. Out of all the specified pipeline stages in the following table, you can register your custom plugins only on Pre- and Post-events. You can’t register plugins on Platform Core Main Operations.
EventStage NameDescription
Pre-EventPre-validationStage in the pipeline for plug-ins that are to execute before the main system operation. Plug-ins registered in this stage may execute outside the database transaction.
Pre-EventPre-operationStage in the pipeline for plug-ins that are to executed before the main system operation. Plugins registered in this stage are executed within the database transaction.
Platform Core OperationMainOperationIntransaction,the main operation of the system, such as create, update, delete, and so on. No custom plug-ins can be registered in this stage. For internal use only.
Post-EventPost-operationStage in the pipeline for plug-ins which are to executed after the main operation. Plug-ins registered in this stage are executed within the database transaction.
Whenever the CRM application invokes an event (like saving or updating a record), the following sequence of actions takes place −
  • The event triggers a Web service call and the execution is passed through the event pipeline stages (pre-event, platform core operations, post-event).
  • The information is internally packaged as an OrganizationRequest message and finally sent to the internal CRM Web service methods and platform core operations.
  • The OrganizationRequest message is first received by pre-event plugins, which can modify the information before passing it to platform core operations. After the platform core operations, the message is packaged as OrganizationResponse and passed to the post-operation plugins. The postoperations plugins can optionally modify this information before passing it to the async plugin.
  • The plugins receive this information in the form of context object that is passed to the Execute method after which the further processing happens.
  • After all the plugin processing completes, the execution is passed back to the application which triggered the event.

Plugin Messages

Messages are the events on which the plugin (or business logic) is registered. For example, you can register a plugin on Create Message of Contact entity. This would fire the business logic whenever a new Contact record is created.
For custom entities, following are the supported messages based on whether the entity is user-owned or organization-owned.
Message NameOwnership Type
AssignUser-owned entities only
CreateUser-owned and organization-owned entities
DeleteUser-owned and organization-owned entities
GrantAccessUser-owned entities only
ModifyAccessUser-owned entities only
RetrieveUser-owned and organization-owned entities
RetrieveMultipleUser-owned and organization-owned entities
RetrievePrincipalAccessUser-owned entities only
RetrieveSharedPrincipalsAndAccessUser-owned entities only
RevokeAccessUser-owned entities only
SetStateUser-owned and organization-owned entities
SetStateDynamicEntityUser-owned and organization-owned entities
UpdateUser-owned and organization-owned entities
For default out-of-the-box entities, there are more than 100 supported messages. Some of these messages are applicable for all the entities while some of them are specific to certain entities. You can find the complete list of supported message in an excel file inside the SDK: SDK\Message-entity support for plug-ins.xlsx

Writing Plugin

In this section, we will learn the basics of writing a plugin. We will be creating a sample plugin that creates a Task activity to follow-up with the customer whenever a new customer is added to the system, i.e. whenever a new Contactrecord is created in CRM.
First of all, you would need to include the references to Microsoft.Xrm.Sdknamespace. The CRM SDK contains all the required SDK assemblies. Assuming that you have already downloaded and installed the SDK in Chapter 2, open Visual Studio. Create a new project of type Class Library. You can name the project as SamplePlugins and click OK.
Mscrm Plugin Create vs Solution
Add the reference of Microsoft.Xrm.Sdk assembly to your project. The assembly is present in SDK/Bin.
Mscrm Plugin Add Solution Reference
Now, create a class named PostCreateContact.cs and extend the class from IPlugin. Till now, your code will look something like the following.
Mscrm Plugin Sample Code
You will also need to add reference to System.Runtime.Serialization. Once you have added the required references, copy the following code inside the PostCreateContact class.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Xrm.Sdk; namespace SamplePlugins { public class PostCreateContact:IPlugin { /// A plug-in that creates a follow-up task activity when a new account is created. /// Register this plug-in on the Create message, account entity, /// and asynchronous mode. public void Execute(IServiceProviderserviceProvider) { // Obtain the execution context from the service provider. IPluginExecutionContext context =(IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext)); // The InputParameters collection contains all the data passed in the message request. if(context.InputParameters.Contains("Target")&& context.InputParameters["Target"]isEntity) { // Obtain the target entity from the input parameters. Entity entity = (Entity)context.InputParameters["Target"]; try { // Create a task activity to follow up with the account customer in 7 days Entity followup = new Entity("task"); followup["subject"] = "Send e-mail to the new customer."; followup["description"] = "Follow up with the customer. Check if there are any new issues that need resolution."; followup["scheduledstart"] = DateTime.Now; followup["scheduledend"] = DateTime.Now.AddDays(2); followup["category"] = context.PrimaryEntityName; // Refer to the contact in the task activity. if(context.OutputParameters.Contains("id")) { Guid regardingobjectid = new Guid(context.OutputParameter s["id"].ToString()); string regardingobjectidType = "contact"; followup["regardingobjectid"] = new EntityReference(rega rdingobjectidType,regardingobjectid); } // Obtain the organization service reference. IOrganizationServiceFactory serviceFactory = (IOrganizationSer viceFactory)serviceProvider.GetService (typeof(IOrganizationServiceFactory)); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); // Create the followup activity service.Create(followup); } catch(Exception ex) { throw new InvalidPluginExecutionException(ex.Message); } } } } }
Following is a step-by-step explanation of what this code does −
Step 1 − Implements the Execute method by taking IServiceProvider object as its parameter. The service provider contains references to many useful objects that you are going to use within plugin.
Step 2 − Obtains the IPluginExecutionContext object using the GetService method of IServiceProvider.
Step 3 − Gets the target entity’s object from the context object’s InputParameters collection. This Entity class object refers to the Contact entity record on which our plugin would be registered.
Step 4 − It then creates an object of Task entity and sets proper subject, description, dates, category and regardingobjectid. The regardingobjectid indicates for which contact record this activity record is being created. You can see that the code gets the id of the parent Contact record using context.OutputParameters and associates it with the Task entity record which you have created.
Step 5 − Creates object of IOrganizationServiceFactory using the IServiceProvider object.
Step 6 − Creates object of IOrganizationService using the IOrganizationServiceFactory object.
Step 7 − Finally, using the Create method of this service object. It creates the follow-up activity which gets saved in CRM.

Signing the Plugin Assembly

This section is applicable only if you are registering your plugin assembly for the first time. You need to sign in the assembly with a key to be able to deploy the plugin. Rightclick the solution and click Properties.
Mscrm Plugin Solution Properties
Select the Signing tab from the left options and check the ‘Sign the assembly’ option. Then, select New from Choose a strong name key file option.
Mscrm Plugin Sign Assembly
Enter the Key file name as sampleplugins (This can be any other name you want). Uncheck the Protect my key file with a password option and click OK. Click Save.
Mscrm Plugin Sign Assembly Add Key
Finally, build the solution. Right Click → Build. Building the solution will generate assembly DLL, which we will use in the next chapter to register this plugin.

Exception Handling in Plugin

More often than not, your plugin logic will need to handle run-time exceptions. For synchronous plugins, you can return an InvalidPluginExecutionException exception, which will show an error dialog box to the user. The error dialog will contain the custom error message that you pass to the Message object of the exception object.
If you look at our code, we are throwing the InvalidPluginExecutionException exception in our catch block.
throw new InvalidPluginExecutionException(ex.Message);

Conclusion

Plugins are definitely crucial to any custom CRM implementation. In this chapter, we focused on understanding the event framework model, pipeline stages, messages, and writing a sample plugin. In the next chapter, we will register this plugin in CRM and see it working from end-to-end scenario.
 

Comments

Popular posts from this blog

Microsoft Dynamics 365 CRM Troubleshooting Solution Import Errors

Remember when CRM life was so much simpler that solutions did not yet exist? If you had separate development and production environments and you wanted to move your customizations, you simply clicked  Export Customizations  and voila! It was done. Those were the days. Nostalgia Warning – in case you’ve forgotten, here’s a screenshot to jog your memory: With CRM 2011, the concept of solutions was introduced, giving us a new set of powers – by picking individual entities, workflows, etc., we now had the ability to group together and move only those customizations we wanted to include in our solution. The next great solutions advancement came with CRM 2016: we can now select specific components within each individual entity – so instead of moving the entire contact entity, for example, we have the option of moving only a certain view or field within the entity. And we can do this without having to hack the xml in the zip file. (By the way, if you want to learn more abou...

How to Filter SubGrid Lookup view in Dynamics 365 CRM

How to Filter SubGrid Lookup view in Dynamics 365 CRM.  Please check the comments in the below code and do follow the steps accordingly and call the  filterSubGrid() funtion on onload. var LastQuery = ""; function filterSubGrid() { debugger; setSubgridLookupFiltering(); } function AddLookupFilter(entity_type, customFilter) { var subgridLookup = Xrm.Page.getControl("lookup_Contacts_Participants"); subgridLookup.addCustomFilter(customFilter, entity_type); } function setSubgridLookupFiltering() { var subgridAddButtonId = "Contacts_Participants_addImageButton"; //Try to get the element from both the current and the parent document. var subgridAddButton = document.getElementById(subgridAddButtonId) || window.parent.document.getElementById(subgridAddButtonId); //This script may run before the subgrid has been fully loaded on the form. If this is the case, //delay and retry until the subgrid has been loaded. if (subg...

How to prevent record from saving in Dynamics CRM using Javascript

  From time to time you might need to add some validation to the save event of an entity, this actually used to be an approach I would use on a regular basis but since the introduction of business rules have found myself doing this less and less. But still, knowing the ability is available is handy. When you define the onsave event function, you must tick the “Pass execution contact as first parameter” option. (See below) Having done that you can create an onSave function with code similar to the example I have shown below. Note forgetting the “(context)”, which will take the context parameter allowing you to prevent the save when needed. function onSave(context) { var saveEvent = context.getEventArgs(); if (Xrm.Page.getAttribute("telephone1").getValue() == null) { // *** Note: I am using an alert for testing a notification maybe better! alert("Put in a phone number!"); saveEvent.preventDefault(); } } Note: This simple example might be better achi...