Tuesday, April 24, 2012

MS CRM 2011: Error during creation of Account record - System.Data.SqlClient.SqlException (0x80131904): Cannot insert duplicate key row in object 'dbo.EmailSearchBase' with unique index 'ndx_for_forward_update'

Today I have got this issue during creation of account. I have googled and found the same issue at CRM Development forum.
I have analyzed plugin’s code and schematically it looked like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;

namespace SamplePlugin
{
    public class Plugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = 
                (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = 
                (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService orgService = factory.CreateOrganizationService(null);

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

            //Some Actions

            target["Field"] = "Value";

            orgService.Update(target);
        }
    }
}


The key points to reproduce error are:

1. Plugin should be registered to handle Create message in Synchronous Post-Operation mode.

2. Entity should be emailable (like contact, account or lead).

3. Email address should be filled at creation form.


Here are possible ways out to solve this issue:

1. For lazy guys who don’t want to update, rebuild and redeploy plugins the way out is to register plugin as Asynchronous.

2. For developers who are not afraid to redesign code it is possible to use following approach:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;

namespace SamplePlugin
{
    public class Plugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = 
                (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = 
                (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService orgService = factory.CreateOrganizationService(null);

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

            //Some Actions

            Entity updatedTarget = new Entity(target.LogicalName)
            {
                Id = target.Id
            };

            updatedTarget["Field"] = "Value";

            orgService.Update(updatedTarget);
        }
    }
}




In case you would not choose 2-nd way you should know about side affect of first approach – plugins that handle Update message for some fields that were passed to current plugin context would be triggered.