Sunday, 29 November 2015

Duplicate detection in Dynamics CRM using plugin

Hi guys!

In this post I will focus on how to write a duplicate detection plugin in Dynamics CRM that would fire on create and on update of a particular entity. You would wonder why we would need a plug-in for that as we already have out of the box duplicate detection rules that can be configured for detecting duplicates. The plug-in based approach is for hard duplicate detection, where the user has no option of saving the record in case a duplicate record based on the provided conditions already exists.

I will describe the algorithm along with code snippets backing up that step in the algorithm. The scenario here is that the user should be prohibited from creating a custom "address" record in case there is already an address record with the same contact.

Note: By comparing attributes I mean those fields that will define whether two records are duplicates. For our case 2 addresses are duplicates if they share the same contact field values. So this is the comparing attribute for our case.

1. Register your plug-in on pre-create and pre-update of the record for which you are configuring the duplicate detection. Include the attributes that are part of the duplicate detection condition in the filtering attributes list. Finally, include these attributes along with the other attributes (whose data you might require even if they are not updated) in the attribute list for the pre-image.

2. Obtain the comparing attributes from the input parameters as here:

 var addressEntity = (Entity)context.InputParameters["Target"];

EntityReference contactEntityReference = null;
 if (addressEntity != null)
                {
                   contactEntityReference = addressEntity.GetAttributeValue<EntityReference>("new_contact");
                }
                else
                {
                    tracingService.Trace("Address entity from target is null");
                }

3. When this plugin runs during update, all the values that are required as comparing attributes may not be present in the input parameters. So obtain them from the pre-image.

// obtain the preimage entity
                Entity preImageEntity = null;
                if (context.PreEntityImages != null)
                {
                    if (context.PreEntityImages.Contains(this.preImageAlias))
                    {
                        preImageEntity = context.PreEntityImages[this.preImageAlias];
                    }
                    else
                    {
                        tracingService.Trace("The pre image entity is not registered.");
                    }
                }
                else
                {
                    tracingService.Trace("The pre image entity is not registered.");
                }

// assign values from the pre-image in case the values are not assigned from the target
                if (preImageEntity != null)
                {
                    if (contactEntityReference == null)
                    {
                        contactEntityReference = preImageEntity.GetAttributeValue<EntityReference>("new_contact");
                    }
                }

4. To validate whether a duplicate record exists that has the same value of the contact in their lookups, we need to do a retrieve multiple and fetch all the records with the same contact details as we have obtained from the input parameter/pre-image. An additional line can be written to avoid the comparison if the contact record is absent in the address record.

 private EntityCollection GetAddressEntitiesBasedOnExistingRecord(Guid recordGuid, string comparingAttributeName, IOrganizationService orgService, ITracingService tracingService)
        {
            QueryExpression query = new QueryExpression("new_address");
            query.ColumnSet = new ColumnSet(new string[] {"new_addressname" });
            query.Criteria.AddCondition(comparingAttributeName, ConditionOperator.Equal, recordGuid);
            EntityCollection results = orgService.RetrieveMultiple(query);
            return results;
        }

where recordGuid is the GUID of the contact record that is present in the address record with which we will be comparing all the other records. The comparing attribute name is the attribute name in the address entity with which we will match the earlier record GUID.

Here is how the function is to be called:

var otherAddressEntities = this.GetAddressEntitiesBasedOnExistingRecord(contactEntityReference.Id, "new_contact", orgService, tracingService);

5. To validate whether a duplicate exists is simply a matter of checking the number of items in the entity collection returned after the previous call:

bool duplicateExists = false;
if(otherAddressEntities  != null && otherAddressEntities.Entities.Count > 0)
{
   duplicateExists = true;
}

6. You can stop the user from performing the save by throwing the InvalidPluginExecutionException and not handling it

if(duplicateExists)
{
   throw new InvalidPluginExecutionException ("A duplicate address record with the same contact already exists. Please select a different contact and try again.");
}


I hope you find this blog useful to help you with your duplicate check and general Dynamics CRM plug-in development. In case you have any feedback or any suggestions to do it better, please feel free to post your comment.

Happy CRMing.
Anurag

Saturday, 28 November 2015

Beginning Dynamics CRM scripting using TypeScript

Hi guys,

The objective of this post is to show how you can better develop your client side JavaScript logic for Dynamics CRM leveraging Type Script. First let me clarify why this will be a boon to us Dynamics CRM developers:
1. Type Script comes with a whole set of reasons to be used to generate the actual JavaScript and this includes type checking, OOP facilitation to name the most important.
2. By adding the latest TypeScript XRM definition file you get IntelliSense support in Visual studio including on how to use your Xrm.Page methods and properties. This is extremely helpful as you would not need to remember anything (code snippets) and auto complete will guide to write the correct code the first time itself.

Here are a few useful links:

1. TypeScript language: http://www.typescriptlang.org/

2. Set up your TypeScript development environment in Visual Studio:

Configure TypeScript in Visual Studio 2012: https://www.microsoft.com/en-in/download/details.aspx?id=34790
Configure TypeScript in Visual Studio 2013: https://www.microsoft.com/en-us/download/details.aspx?id=48739
Configure TypeScript in Visual Studio 2015: https://www.microsoft.com/en-us/download/details.aspx?id=48593

Note: Visual Studio 2015 comes preloaded with TypeScript project templates.
TypeScript development is also supported in other IDEs, please refer to the appropriate section in the above link

3. Developing TypeScript using Visual Studio and syntax basics: http://developer.telerik.com/featured/typescript-with-visual-studio-2015/

You can start with the "Using TypeScript with Visual Studio 2015" sub section.

4. Add the latest Xrm TypeScript definition file to your project in Visual Studio:

Once you have the TypeScript project template you can follow the below steps to add the Xrm definition file to the project.
4.1. Expand the project tab and then right click the References tab then select "Manage NuGet Packages" as shown below



4.2. In the NuGet Package Manager window that opens type "xrm definitely typed" in the search box and select the version of the XRM definition file based on the SDK version you will be coding against as shown below














4.3. Select the version (ideally the latest stable release) and click on Install
                                                                                 













4.4. Select OK on the confirmation dialog that comes to make the required changes to the solution as below



4.5. You can see the TypeScript definition file added to your project as below


5. Now you can start writing your scripts in TypeScript in the app.ts file or any other TypeScript file that you may have added.

The below image shows how you can get IntelliSense support in Visual Studio for your scripts:


Here is a sample TypeScript snippet that I have written to get the value of a whole number field


The corresponding JavaScript file that gets generated with the JavaScript code can be found by clicking on the "Show All Files" button on the Solution Explorer as below:



The file will have the same name as the TypeScript file, in our case "app" and the extension will be ".js", since it is a pure JavaScript file. Here is the JavaScript equivalent code for the snippet presented in the previous snippets.


Note: I have set "Compile on save" option in the TypeScript build options for the project.So the JavaScript gets updated the moment I save the TypeScript file.

You can see these options by right clicking the project and then selecting properties


Then  go to "TypeScript Build" tab and you will see the options that you can set/unset based on your liking.