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
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