none
CRM 2011 Online - Stop Transactions in Plugins

    Question

  • Hi,

    Imagine the following scenario (CRM 2011 Online):

    - I have a sync, Post plugin, that when a account is created, creates a contact. If it fails to create the contact, when I catch the exception I want to create a record in my "new_CrmOperationLog" entity, and then throw a exception back to the user, telling the Account creation failed because I couldn't create the contact.

    The problem is that since CRM 2011 sync plugins run in-transaction (except for pre-validation), when I present the message to the user via throwing a InvalidPluginException, the transaction mechanism rolls back the operations and deletes my record in the new_CrmOperationLog entity.

     

    Any way to prevent or overcome this? 

     

    Thanks in advance,

    Pedro

     


    Tuesday, July 12, 2011 3:30 PM

All replies

  • Everything you do inside the plugin will be rolled back if you throw. Your only option would be to start a new transaction by instantiating a new proxy to call CRM instead of using the IOrganizationServiceFactory that is in the plugin context.

     


    BTW, Have you considered using the ITracingService instead of creating an error log entity?


    Gonzalo | gonzaloruizcrm.blogspot.com

    Tuesday, July 12, 2011 5:48 PM
    Moderator
  • Hi Gonzalo,

     

    Thanks for your reply. I had the idea of calling a external webservice to do so, but it ain't suitable here because my cliente doesn't have a webserver to host it, neither they want to use Azure. Is there a way to instantiate a new proxy using context credentials?

    I'm also using the ITracingService, but the idea was to keep every error in a new Activity entity (to make use of regarding object), called System Errors.

     

    My current code is as follows:

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

     

                // Get a reference to the tracing service.

                ITracingService trace = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

     

                //ATENÇÃO - Para alguns eventos isto (e consequentemente o registo do erro), tem de se mudar para um Moniker

                Entity entity = null;

                if (context != null && context.InputParameters != null && context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)

                {

                    // Obtain the target entity from the input parmameters.

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

                }

                try

                {                

                    // Plug-in business logic goes below this line.               

     

                    // Invoke organization service methods.                

                }

                catch (FaultException<OrganizationServiceFault> ex)

                {

                    #region FaultException

     

                    //Vai buscar os details do erro

                    System.Text.StringBuilder errors = ex.Detail.ErrorDetails.Aggregate(

                    new System.Text.StringBuilder(), (list, error) =>

                    {

                        list.Append(string.Format("[{0}:{1}]", error.Key, error.Value));

                        return list;

                    });

     

                    //Constroi a mensagem de erro.

                    string erro = string.Format(

    @"Code: {0}

    TimeStamp: {4}

    ErrorDetails: {1}

    Message: {2}

    Trace: {3}

    Inner Fault: {5}",

                        ex.Detail.ErrorCode,

                        errors.ToString(),

                        ex.Detail.Message,

                        ex.Detail.TraceText,

                        ex.Detail.Timestamp,

                        null != ex.Detail.InnerFault ? ex.Detail.InnerFault.Message : "Empty");

     

                    trace.Trace(erro);

     

                    //ATENÇÃO - No Guid da entidade do erro só podem ser passadas entidades que tenham uma relação com as activities.

                    //caso não tenham, deve utilizar-se uma entidade relacionada, que faça sentido, e que tenha activities (ex.: Empresa)

                    //ou, se, e só se, não for aplicável, usado o vazio.                

                    if (entity == null)

                    {

                        RegisterCrmErrors.EscreveNoLog(

                            MethodBase.GetCurrentMethod().DeclaringType.FullName,

                            erro,

                            RegisterCrmErrors.Tipos.Automatismo,

                            service,

                            null,

                            "");

                    }

                    else

                    {

                        RegisterCrmErrors.EscreveNoLog(

                            MethodBase.GetCurrentMethod().DeclaringType.FullName,

                            erro,

                            RegisterCrmErrors.Tipos.Automatismo,

                            service,

                            null,

                            "");

                    }

     

                    //Temporáriamente desabilitado pois perdemos o registo de erro de sistema se usarmos, dado que faz rollback automático

                    //throw new InvalidPluginExecutionException("Ocorreu um erro de CRM na execução do Plug-In.", ex);

     

                    #endregion

                }

                catch (System.TimeoutException ex)

                {

                    #region TimeOut

     

                    //Constroi a mensagem de erro.

                    string erro = string.Format(

    @"Message: {0}

    Stack Trace: {1}                    

    Inner Exception: {2}",

                        ex.Message,

                        ex.StackTrace,

                        null != ex.InnerException ? ex.InnerException.Message : "Empty");

     

                    trace.Trace(erro);

     

                    //ATENÇÃO - No Guid da entidade do erro só podem ser passadas entidades que tenham uma relação com as activities.

                    //caso não tenham, deve utilizar-se uma entidade relacionada, que faça sentido, e que tenha activities (ex.: Empresa)

                    //ou, se, e só se, não for aplicável, usado o vazio.

                    if (entity != null)

                    {

                        RegisterCrmErrors.EscreveNoLog(

                            MethodBase.GetCurrentMethod().DeclaringType.FullName,

                            erro,

                            RegisterCrmErrors.Tipos.Automatismo,

                            service,

                            entity.Id,

                            entity.LogicalName);

                    }

                    else

                    {

                        RegisterCrmErrors.EscreveNoLog(

                            MethodBase.GetCurrentMethod().DeclaringType.FullName,

                            erro,

                            RegisterCrmErrors.Tipos.Automatismo,

                            service,

                            null,

                            "");

                    }

     

                    //throw new InvalidPluginExecutionException("Ocorreu um erro de timeout na execução do Plug-In.", ex);

     

                    #endregion

                }

                catch (System.Exception ex)

                {

                    #region Erro Genérico

                    string erro = string.Format(

    @"Message: {0}

    Stack Trace: {1}                    

    Inner Exception: {2}",

                        ex.Message,

                        ex.StackTrace,

                        null != ex.InnerException ? ex.InnerException.Message : "Empty");

     

                    // Display the details of the inner exception.

                    if (ex.InnerException != null)

                    {

                        Console.WriteLine(ex.InnerException.Message);

     

                        FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException

                            as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;

                        if (fe != null)

                        {

                            //Vai buscar os details do erro

                            System.Text.StringBuilder errors = fe.Detail.ErrorDetails.Aggregate(

                            new System.Text.StringBuilder(), (list, error) =>

                            {

                                list.Append(string.Format("[{0}:{1}]", error.Key, error.Value));

                                return list;

                            });

     

                            erro += string.Format(

    @"\nInner Exception Details:

    Code: {0}

    TimeStamp: {4}

    ErrorDetails: {1}

    Message: {2}

    Trace: {3}

    Inner Fault: {5}",

                                    fe.Detail.ErrorCode,

                                    errors.ToString(),

                                    fe.Detail.Message,

                                    fe.Detail.TraceText,

                                    fe.Detail.Timestamp,

                                    null != fe.Detail.InnerFault ? fe.Detail.InnerFault.Message : "Empty");

                        }

                    }

     

                    trace.Trace(erro);

     

                    //ATENÇÃO - No Guid da entidade do erro só podem ser passadas entidades que tenham uma relação com as activities.

                    //caso não tenham, deve utilizar-se uma entidade relacionada, que faça sentido, e que tenha activities (ex.: Empresa)

                    //ou, se, e só se, não for aplicável, usado o vazio.

                    if (entity != null)

                    {

                        RegisterCrmErrors.EscreveNoLog(

                            MethodBase.GetCurrentMethod().DeclaringType.FullName,

                            erro,

                            RegisterCrmErrors.Tipos.Automatismo,

                            service,

                            entity.Id,

                            entity.LogicalName);

                    }

                    else

                    {

                        RegisterCrmErrors.EscreveNoLog(

                            MethodBase.GetCurrentMethod().DeclaringType.FullName,

                            erro,

                            RegisterCrmErrors.Tipos.Automatismo,

                            service,

                            null,

                            "");

                    }

     

                    //throw new InvalidPluginExecutionException("Ocorreu um erro genérico na execução do Plug-In.", ex);

     

                    #endregion

                }          

            }

     

    Tuesday, July 12, 2011 5:56 PM
  • It should be possible to instantiate a new proxy using the credentials, (or you can always hard code the credentials if need be), but I have no experience doing so for CRM Online. In my honest opinion it is a bit unnecessary to have those error records if you are already using the ITracingService and it will add a lot of complexity and cost to your solution...

    Gonzalo | gonzaloruizcrm.blogspot.com

    Tuesday, July 12, 2011 6:10 PM
    Moderator
  • Maybe too late, but you could just catch the exception, log your entry and DON'T throw an additinal InvalidPluginException.

            private void HandleViewException(Exception ex, string info) {
                try {
                    //Call your custom logging-function...
                    WriteErrorLog(ex, info);
                } catch (Exception ex_) {
                    throw new InvalidPluginExecutionException("Error logging value", ex_);
                }
            }

    Cheers,

    mk

    Friday, March 02, 2012 4:56 PM
  • Hi,

    Will the behavior be same for the timeout occurs on PostCreate as it rollbacks everything for explicit throw of exception?

    Ex: Consider on PostCreate of account we are creating contacts, and timeout occur, so will the creating of Contacts and the account will be rollback?

    Thanks

    Kind Regards,

    Abdul


    • Edited by Abdul Raziq Thursday, July 27, 2017 5:35 AM added example
    Thursday, July 27, 2017 5:32 AM