Duplication Detection using CRM SDK

Although CRM provides a duplicate detection process that can be configured using the User Interface, many time when developing application for CRM, such as Import Manager or Synchronization application, you require to create your own duplicate detection rules, execute the duplicate detection process and retrieve the results of the duplicates from within your application.

The code samples below show how to create a Duplicate Detection Rule, execute the duplicate detection process and retrieve the results of the duplicate records.

To create a Duplicate Detection Rule, we use the DuplicateRule class, and provide a name for the rule and the entity name that will be used for the rule. In the example below, we create a rule that will check for duplicate email addresses in the contact record. After creating the rule, we need to specify the condition for the rule. The DuplicateRuleCondition class allows us to specify the attribute for the condition, and the type of rule that will be used. In the example below we use the emailaddress1 field and the base and matching attribute, and we set the match as exact match (an OptionSet value of 0).

When the rule and the rule condition have been created, we publish the rule by calling the execute method of the CRM service with the PublishDuplicateRuleRequest and PublishDuplicateRuleResponse.

Note that the process to publish a duplicate detection rule can be someone of a lengthy process, so we call the WaitForAsyncJobToFinish method to wait until the job is complete.

private Guid CreateDuplicateDetectionRule()


    DuplicateRule rule = new DuplicateRule()


        Name = "Contacts with the same Email Address",

        BaseEntityName = Contact.EntityLogicalName,

        MatchingEntityName = Contact.EntityLogicalName


    rule.Id = CRMHelper.xrm5.Create(rule);

    DuplicateRuleCondition emailCondition = new DuplicateRuleCondition()


        BaseAttributeName = "emailaddress1",

        MatchingAttributeName = "emailaddress1",

        OperatorCode = new OptionSetValue(0), // value 0 = 'exact match'

        RegardingObjectId = rule.ToEntityReference()



    PublishDuplicateRuleRequest publishRequest = new PublishDuplicateRuleRequest()


        DuplicateRuleId = rule.Id


    PublishDuplicateRuleResponse publishResponse =


    WaitForAsyncJobToFinish(publishResponse.JobId, 60);

    return rule.Id;


The WaitForAsyncJobToFinish method call queries the AsyncOperation entity every 1 second to check if the process has been completed.

Once the process has been completed, the method exits and application can continue processing.

Note that since this is a lengthy process, additional code can be added in case it takes more than the maximum second value.
Normally the 60 seconds passed from the previous method is more than sufficient to create a Duplicate Detection Rule, but at times based on environment, more time will be needed.

private void WaitForAsyncJobToFinish(Guid jobId, int maxSeconds)


    for (int i = 0; i < maxSeconds; i++)


        var asyncJob = service.Retrieve(AsyncOperation.EntityLogicalName,

                jobId, new ColumnSet("statecode")).ToEntity<AsyncOperation>();

    if (asyncJob.StateCode.HasValue && asyncJob.StateCode.Value == AsyncOperationState.Completed)





The CheckDuplicateEmails method shown below is the entry point of the application. The method will first call the CreateDuplicateDetectionRule before proceeding with the running the duplicate detection process. If you already have a Duplicate Detection Rule in your environment, you can skip calling the CreateDuplicateDetectionRule method and the Delete method at the end of the method.
We create a new BulkDetectDuplicatesRequest, passing a job name, query expression that specifies the name of the entity and columns to return, a recurrence pattern and start time. The ToRecipients and CCRecipients are required fields as part of the request (if omitted, process will fail). We call the execute method passing the BulkDetectDuplicatesRequest, and get a BulkDetectDuplicatesResponse class. Since this process is also asynchronous, we will call the WaitForAsyncJobToFinish method passing the job identifier and the amount of seconds to wait.
After the Bulk Detect Duplicates process has been complete we can query the DuplicateRecord entity passing the asyncoperationid field to retrieve a list of all the duplicate contact records that were detected. The method will the return the duplicate identifiers to the calling method.
As previously mentioned, the Delete call should be omitted if you are using an existing Duplicate Detection Rule.

public List<Guid> CheckDuplicateEmails()


    Guid ruleId = CreateDuplicateDetectionRule();

    BulkDetectDuplicatesRequest request = new BulkDetectDuplicatesRequest()


        JobName = "Detect Duplicate Contacts",

        Query = new QueryExpression()


            EntityName = Contact.EntityLogicalName,

            ColumnSet = new ColumnSet(true)


        RecurrencePattern = String.Empty,

        RecurrenceStartTime = DateTime.Now,

        ToRecipients = new Guid[0],

        CCRecipients = new Guid[0]


    BulkDetectDuplicatesResponse response = (BulkDetectDuplicatesResponse)service.Execute(request);

    WaitForAsyncJobToFinish(response.JobId, 120);

    QueryByAttribute query = new QueryByAttribute()


        ColumnSet = new ColumnSet(true),

        EntityName = "duplicaterecord"




    EntityCollection results = service.RetrieveMultiple(query);

    var duplicateIds = results.Entities.Select((entity) => ((DuplicateRecord)entity).BaseRecordId.Id);

    service.Delete(DuplicateRule.EntityLogicalName, ruleId);

    return duplicateIds.ToList<Guid>();


Finally, if we need to use the duplicate identifiers that were retrieved from the CheckDuplicateEmails method, we can use the code below to display them in a grid.
The code sample below uses the Infragistics Grid, but any grid control can be used to display the results.

List<Guid> duplicates = CRMHelper.crm.CheckDuplicateEmails();

if (duplicates.Count > 0)


    foreach (Guid contactid in duplicates)


        Entity contact = CRMHelper.crm.RetrieveContact(contactid);

        int rowid = dgvDuplicates.Rows.Add();

        DataGridViewRow row = (DataGridViewRow)dgvDuplicates.Rows[rowid];

        row.Cells[0].Value = contactid.ToString();

        row.Cells[2].Value = contact.Attributes["fullname"].ToString();

        row.Cells[3].Value = contact.Attributes["emailaddress1"].ToString();