Bypass Custom Business Logic

There are times when you want to be able to perform data operations without having custom business logic applied. These scenarios typically involve bulk data operations where large numbers of records are being created, updated or deleted.

As a developer of a client application, you can pass special optional parameters with your requests to control two types of custom business logic as described in the following table:

Logic type When to use
Synchronous Logic To enable bulk data operation to be completed as quickly as possible. Bypass synchronous logic when the data you're changing is known to meet the requirements of the organization or you have a plan to achieve this logic by other means. Bypass all custom synchronous logic so that each operation can complete faster, shortening the total time of the bulk operation.
Power Automate Flows When large numbers of system jobs created to support flows cause a backup within Dataverse that can impact performance. You can mitigate this performance issue by not triggering the flows while performing bulk operations.

Bypass Synchronous Logic

Use the BypassCustomPluginExecution optional parameter to bypass custom synchronous logic.

The alternative to using this optional parameter is to locate and disable the custom plug-ins that contain the synchronous business logic. But disabling plug-ins means that the logic is disabled for all users while those plug-ins are disabled. It also means that you have to take care to only disable the right plug-ins and remember to re-enable them when you're done.

Using the optional parameter allows you to disable custom synchronous plug-ins for specific requests sent by an application configured to use this option.

There are two requirements:

  • You must send the requests using the BypassCustomPluginExecution optional parameter.
  • The user sending the requests must have the prvBypassCustomPlugins privilege. By default, only users with the system administrator security role have this privilege.

Note

The prvBypassCustomPlugins is not available to be assigned in the UI at this time. You can add a privilege to a security role using the API. More information: Adding the prvBypassCustomPlugins privilege to another role

What does BypassCustomPluginExecution do?

This solution targets the custom synchronous business logic that has been applied for your organization. When you send requests that bypass custom business logic, all synchronous plug-ins and real-time workflows are disabled except:

  • Plug-ins that are part of the core Microsoft Dataverse system or part of a solution where Microsoft is the publisher.
  • Workflows included in a solution where Microsoft is the publisher.

System plug-ins define the core behaviors for specific entities. Without these plug-ins, you would encounter data inconsistencies that may not be easily fixed.

Solutions shipped by Microsoft that use Dataverse such as Microsoft Dynamics 365 Customer Service, or Dynamics 365 Sales also include critical business logic that can't be bypassed with this option.

Important

You may have purchased and installed solutions from other Independent Software Vendors (ISVs) which include their own business logic. The synchronous logic applied by these solutions will be bypassed. You should check with these ISVs before you use this option to understand what impact there may be if you use this option with data that their solutions use.

How do I use the BypassCustomPluginExecution option?

You can use this option with either the SDK for .NET or the Web API.

There are two ways to use this optional parameter with the SDK for .NET.

Set the value as an optional parameter

The following example sets the optional BypassCustomPluginExecution parameter when creating a new account record using the CreateRequest class.

static void DemonstrateBypassCustomPluginExecution(IOrganizationService service)
{
    Entity account = new("account");
    account["name"] = "Sample Account";

    CreateRequest request = new()
    {
        Target = account
    };
    request.Parameters.Add("BypassCustomPluginExecution", true);
    service.Execute(request);
}

You can use this method for data operations you initiate in your plug-ins when the calling user has the prvBypassCustomPlugins privilege.

Set the CrmServiceClient.BypassPluginExecution property

The following example sets the CrmServiceClient.BypassPluginExecution Property when creating a new account record:

var service = new CrmServiceClient(connectionString);  

service.BypassPluginExecution = true;

var account = new Entity("account");
account["name"] = "Sample Account";

service.Create(account);

Because this setting is applied to the service, it remains set for all requests sent using the service until it's set to false.

Note

This property is not available in the Dataverse.Client.ServiceClient, but it is available on the Dataverse.Client.Extensions.CRUDExtentions methods.

Adding the prvBypassCustomPlugins privilege to another role

Because the prvBypassCustomPlugins privilege isn't available in the UI to set for different security roles, if you need to grant this privilege to another security role you must use the API. For example, you may want to grant this privilege to a user with the system customizer security role.

The prvBypassCustomPlugins privilege has the ID 148a9eaf-d0c4-4196-9852-c3a38e35f6a1 in every organization.

Associate the prvBypassCustomPlugins privilege to the security role using AddPrivilegesRoleRequest.

static void AddprvBypassCustomPluginsToRole(IOrganizationService service, Guid roleId)
{
    var request = new AddPrivilegesRoleRequest
    {
        RoleId = roleId,
        Privileges = new[]{
            new RolePrivilege{
                PrivilegeId = new Guid("148a9eaf-d0c4-4196-9852-c3a38e35f6a1"),
                Depth = PrivilegeDepth.Global
            }
        }
    };
    service.Execute(request);
}

Frequently asked questions for bypassing synchronous logic (FAQ)

Following are frequently asked questions about using the BypassCustomPluginExecution optional parameter to bypass synchronous business logic.

Does BypassCustomPluginExecution bypass plug-ins for data operations by Microsoft plug-ins?

No. If a synchronous plug-in or real-time workflow in a Microsoft solution performs operations on other records, the logic for those operations aren't bypassed. Only those synchronous plugins or real-time workflows that apply to the specific operation are bypassed.

Can I use BypassCustomPluginExecution for data operations I perform within a plug-in?

Yes, but only when the plug-in is running in the context of a user who has the prvByPassPlugins privilege. For plug-ins, set the optional BypassCustomPluginExecution parameter on the class derived from OrganizationRequest Class. You can't use the CrmServiceClient or ServiceClient classes in a plug-in.

What about asynchronous plug-in steps, asynchronous workflows and flows?

Asynchronous logic isn't bypassed. Asynchronous logic doesn't significantly contribute to the cost of processing the records, therefore it isn't by passed by this parameter.

Bypass Power Automate Flows

Power Automate flows can respond to Dataverse events using the When a row is added, modified or deleted or When an action is performed triggers. When these events occur, Dataverse creates system jobs to execute these flows.

When a program or plug-in performs bulk operations, a large number of system jobs may be created. A large number of system jobs can cause performance issues for Dataverse. You can choose to bypass creating these system jobs in your program or plug-in by using the SuppressCallbackRegistrationExpanderJob optional parameter.

The CallbackRegistration table manages flow triggers, and there's an internal operation called expander that creates the system jobs.

Note

When this option is used, the flow owners will not receive a notification that their flow logic was bypassed.

When to bypass Power Automate Flows

Important

Don't use the SuppressCallbackRegistrationExpanderJob optional parameter unless you know that the performance issues you are experiencing are because of a large number of specific system jobs that are created.

People have added flows for business reasons and they shouldn't be bypassed without careful consideration. Be sure to consider the Mitigation strategies mentioned below.

Will SuppressCallbackRegistrationExpanderJob help you?

Use this option only if you see performance issues after bulk operations occur and you have a large number of CallbackRegistration Expander Operation system jobs with a StatusCode set to 0 : Waiting for Resources.

You can use the following queries to get information about the status of these jobs.

If the total count is greater than 50,000, these queries return the following error.

Name: AggregateQueryRecordLimitExceeded
Code: 0x8004E023
Number: -2147164125
Message: The maximum record limit is exceeded. Reduce the number of records.

Note

If the queries do not return an error, the number of queued jobs is not likely to be the issue. Typically, the number of queued jobs exceeds 50,000 records before performance issues will occur.

The following examples output the number of CallbackRegistration Expander Operation system jobs by the state code. The operationtype value for this kind of system job is 79.

static void RetrieveCallbackRegistrationExpanderStatus(IOrganizationService service)
{
    string fetchXml = @"<fetch aggregate='true'>
        <entity name='asyncoperation'>
        <attribute name='statuscode' alias='statuscode' groupby='true' />
        <attribute name='statuscode' alias='count' aggregate='count' />
        <filter>
            <condition attribute='operationtype' operator='eq' value='79' />
        </filter>
        </entity>
    </fetch>";

    FetchExpression fetchExpression = new(fetchXml);

    EntityCollection response = service.RetrieveMultiple(fetchExpression);

    foreach (Entity result in response.Entities)
    {
        string statusCode = result.FormattedValues["statuscode"];
        int count = (int)((AliasedValue)result["count"]).Value;
        Console.WriteLine($"{statusCode}: {count}");
    }
}

Output:

Canceled: 4101
Failed: 13
Waiting for Resources: 50,000

How to bypass Power Automate flows

How you bypass flows depends on whether you're using the SDK for .NET or Web API.

Note

For data operations initiated within plug-ins, you must use the SDK for .NET.

The following examples create an account record that won't trigger Power Automate.

static void DemonstrateSuppressCallbackRegistrationExpanderJob(IOrganizationService service)
{
    Entity account = new("account");
    account["name"] = "Sample Account";

    CreateRequest request = new()
    {
        Target = account
    };
    request.Parameters.Add("SuppressCallbackRegistrationExpanderJob", true);
    service.Execute(request);
}

Mitigation strategies

Flow owners expect their logic to be executed. Flow owners won't be notified that their logic was bypassed when you use this option. It's important to communicate to flow owners that the logic wasn't applied so that they know when and why their logic wasn't applied. They can then determine whether or how to apply their logic.

People can create child flows that contain logic that can be invoked by multiple triggers, even manually. If the logic is contained within a child flow, it may be triggered by other means later. More information Create child flows

Identify flows that will be bypassed

You may not be able to identify exactly which flows will be bypassed. You can query the CallbackRegistration table table to assess how much impact there will be and who to contact about their flows not running. The following table describes some CallbackRegistration table columns that are useful;

Column Description
name If this value is a GUID value, it should match the flowid value and you should be able to view the flow definition in a URL with this value by adding it to this URL: https://make.powerautomate.com/environments/<environmentid>/flows/<flowid>/details.
message When the flow uses the When a row is added, modified or deleted trigger, it may subscribe to all the combinations of Create, Update, and Delete operations with these options:
- 1: Added
- 2: Deleted
- 3: Modified
- 4: Added or Modified
- 5: Added or Deleted
- 6: Modified or Deleted
- 7: Added or Modified or Deleted
sdkmessage When the flow uses the When an action is performed trigger, this column contains the name of the message.
scope Flows only apply to the scope specified by the user as defined using these options:
- 1: User
- 2: BusinessUnit
- 3: ParentChildBusinessUnit
- 4: Organization
ownerid The owner of the callback registration and the flow.
softdeletestatus Whether the flow is deleted. 0 isn't deleted. 1 is deleted.

The following example queries return these values:

static void RetrieveCallbackOperations(IOrganizationService service)
{

    QueryExpression callbackRegistrationQuery = new("callbackregistration")
    {
        ColumnSet = new ColumnSet("name", "entityname", "message", "sdkmessagename", "scope", "ownerid"),
        Criteria = new FilterExpression(LogicalOperator.And)
        {
            Conditions = {
                { new ConditionExpression("softdeletestatus",ConditionOperator.Equal,0) },
                // Add more conditions here to filter the results
            }
        }
    };

    EntityCollection callbackRegistrations = service.RetrieveMultiple(callbackRegistrationQuery);

    foreach (Entity callbackRegistration in callbackRegistrations.Entities)
    {
        string ownerid = callbackRegistration.FormattedValues["ownerid"];
        string scope = callbackRegistration.FormattedValues["scope"];
        string name = callbackRegistration.GetAttributeValue<string>("name");
        string message = callbackRegistration.FormattedValues["message"];
        string entityname = callbackRegistration.GetAttributeValue<string>("entityname");
        string sdkmessage = callbackRegistration.GetAttributeValue<string>("sdkmessagename");

        Console.WriteLine($"{ownerid},{scope},{name},{message},{entityname},{sdkmessage},");
    }
}

Output

FirstName LastName,Organization,de7153ba-9221-4079-82cc-c884bbd05dc0,Modified,account,,
FirstName LastName,Organization,Callback Registration Id: b44090aa-adde-4866-ac2e-d68fbcbe7d5a,Added,account,,
FirstName LastName,Organization,Callback Registration Id: dabfa1a1-b794-44d0-ad34-cd49ea650606,Added,none,sample_BusinessEvent,

Frequently asked questions about bypassing Power Automate flows (FAQ)

Following are frequently asked questions about using the SuppressCallbackRegistrationExpanderJob optional parameter to bypass Power Automate flows.

Do users need a special privilege?

No. Unlike Bypass Synchronous Logic, no special privilege is required.

If my client application uses this optional parameter, will any operations performed by plug-ins registered against the operation also apply it?

No. The parameter isn't passed through to any operations performed by plug-ins that are registered for the events that occur because of requests from your client application. If you want to bypass flows for operations performed by plug-ins, you must use the SuppressCallbackRegistrationExpanderJob optional parameter in your plug-in code.

See also

Web API: Compose HTTP requests and handle errors
Use optional parameters