Dataverse – Ignorar plugins customizados sincronos (Bypass custom sync plugins)


Olá pessoal,

Passando para compartilhar uma nova funcionalidade do Dataverse que na verdade era um pedido bem antigo, irei falar sobre como ignorar regras de negócio customizadas sincronas ou melhor dizendo bypass custom synchronous business rules!

Bom, primeiro vamos a necessidade…

Atualmente para “escaparmos” de ter que executar milhões e milhões de registros durante uma migração de dados, custumamos desativar manualmente ou através de um pouco de código todos/ou a maioria do plugins, pois na maiorias das vezes estamos apenas inserindo os dados, não queremos aplicar nenhuma regra de negócio específica.

A possibilidade que ganhamos…

Agora podemos apenas indicar que os registros a serem inseridos/atualizados a seguir não deverão persistir nenhuma regra de negócio sincrona, desta forma, nenhuma desativação de plugins precisa ser feita.

Coisas para ter em mente

  • Apenas plugins e workflows sincronos (tempo real) podem fazer use desta técnica
  • Apenas customizações/extensões podem usar o bypass, ou seja, qualquer funcionalidade core/nativa não será impactada
  • Cuidado com soluções de terceiros, você poderá prejudicar alguma funcionalidade se usar o bypass. Verifique a documentação destas ferramentas antes de fazer uso
  • Apenas administradores do sistema ou usuários que foram intencionalmente autorizados através de atribuição de direitos de acesso poderão fazer uso desta funcionalidade
  • Não podemos usar o bypass através do métodos mais comuns da IOrganisationService interface, para usar o bypass devemos fazer as chamadas através do método Execute ou utilizar a classe CrmServiceClient se você não está trabalhando dentro do contexto do Dynamics

Outra ideia uso além da migração de dados

Em certos casos precisamos que quando um plugin seja executado, os demais plugins que poderão ser acionados com as mudanças deste primeiro não sejam executadas. No passados fazemos isso usando flags, assim se as flags que fazem a gestão do que será executado ou não. Com o bypass, em alguns cenários, podemos fazer uso sem necessitar das flags. Pode não ser eficaz em 100% dos cenários, pois pode causar uns problemas para depurrar o código de desenvolvedores desatentos, mas sem dúvido. algo para ser considerado!

Exemplo prático

Criei um solução (disponível no meu GitHub), para exemplificar o que o bypass pode fazer. Existem dois plugin sincronos, um registrado na criação de uma conta e outro na criação de um contato.

O plugin da conta apenas cria um contato, porém o parâmetro ByPassCustomPluginExecution foi informado, desta forma. o segundo plugin (contato) não deverá ser executado. Vale lembrar que testei e estou assumindo que o usuário que está executando os plugins é um administrador do sistema.

Uma olhada nos códigos!

Conta:

using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;

namespace TMC.ByPassSyncBusinessRules
{
    public class Account : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            // Obtain the tracing service
            ITracingService tracingService =
            (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the execution context from the service provider.  
            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));

            // The InputParameters collection contains all the data passed in the message request.  
            if (context.InputParameters.Contains("Target") &&
                context.InputParameters["Target"] is Entity)
            {
                // Obtain the target entity from the input parameters.  
                Entity entity = (Entity)context.InputParameters["Target"];

                // Obtain the organization service reference which you will need for  
                // web service calls.  
                IOrganizationServiceFactory serviceFactory =
                    (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

                tracingService.Trace($"Account Plugin Start");

                try
                {
                    // Plug-in business logic goes here.  
                    Entity contact = new Entity("contact");
                    contact["parentcustomerid"] = entity.ToEntityReference();
                    contact["firstname"] = "Test";
                    contact["lastname"] = "123";

                    var createRequest = new CreateRequest
                    {
                        Target = contact
                    };
                    
                    // Bypass set to TRUE (Contact Plugin will be skipped)
                    createRequest.Parameters.Add("BypassCustomPluginExecution", true);

                    service.Execute(createRequest);
                }

                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException($"An error occurred in Account Plugin {ex.ToString()}");
                }

                catch (Exception ex)
                {
                    tracingService.Trace($"Account Plugin Error: {ex.ToString()}");
                    throw;
                }
                finally
                {
                    tracingService.Trace($"Account Plugin End");
                }
            }
        }
    }
}

Contato:

using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;

namespace TMC.ByPassSyncBusinessRules
{
    public class Contact : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            // Obtain the tracing service
            ITracingService tracingService =
            (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the execution context from the service provider.  
            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));

            // The InputParameters collection contains all the data passed in the message request.  
            if (context.InputParameters.Contains("Target") &&
                context.InputParameters["Target"] is Entity)
            {
                // Obtain the target entity from the input parameters.  
                Entity entity = (Entity)context.InputParameters["Target"];

                // Obtain the organization service reference which you will need for  
                // web service calls.  
                IOrganizationServiceFactory serviceFactory =
                    (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

                tracingService.Trace($"Contact Plugin Start");

                try
                {
                    // Plug-in business logic goes here.
                    Entity task = new Entity("task");
                    task["regardingobjectid"] = entity.ToEntityReference();
                    task["subject"] = "Task associated to the current contact";

                    var createRequest = new CreateRequest
                    {
                        Target = task
                    };

                    service.Execute(createRequest);
                }

                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException($"An error occurred in Contact Plugin {ex.ToString()}");
                }

                catch (Exception ex)
                {
                    tracingService.Trace($"Contact Plugin Error:  {ex.ToString()}");
                    throw;
                }
                finally
                {
                    tracingService.Trace($"Contact Plugin End");
                }
            }
        }
    }
}

Ao criar um nova conta, podemos observar que o contato “Test 123″ foi criado, porém o plugin do contato não foi acionado, desta forma, a tarefa ” Task associated to the current contact” não foi criada!

Bom é isso, como falei o código completo está no meu GitHub!

[]’s,

Tiago

Deixe um comentário

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.