src/Client/Invoke-XrmBulkRequests.ps1
|
<# .SYNOPSIS Split and Execute Multiple Organization Requests. .Description Send requests to Microsoft Dataverse for bulk execution, in ExecuteMultiple batches of BatchSize requests. Without ContinueOnError, the first fault stops the processing and raises an error that names the faulted request (global index and request name); the requests before it were executed. With ContinueOnError, every request is processed. The faults are reported at the end in one non-terminating error whose TargetObject holds one object per fault: Index (0-based, global to the Requests array), Count (1, or the batch size when a whole batch was refused), RequestName and Message. Capture them with -ErrorVariable. .PARAMETER XrmClient Xrm connector initialized to target instance. Use latest one by default. (Dataverse ServiceClient) .PARAMETER Requests Array of organization requests to execute. .PARAMETER BatchSize Number of requests sent in each ExecuteMultiple call, from 1 to 1000. (Default: 500) .PARAMETER ContinueOnError Indicates whether to continue with the next requests when a request fails. (Default: false = stop at the first fault) .PARAMETER ReturnResponses Indicates if a response is returned for each request. (Default: false = No response) .PARAMETER Quiet Do not log a line for each batch. .PARAMETER BypassCustomPluginExecution Added to every request: legacy bypass of synchronous custom plug-ins. .PARAMETER BypassBusinessLogicExecution Added to every request: custom business logic to bypass (CustomSync, CustomAsync). .PARAMETER BypassBusinessLogicExecutionStepIds Added to every request: ids of the plug-in steps to bypass. .PARAMETER SuppressCallbackRegistrationExpanderJob Added to every request: do not trigger Power Automate flows. .PARAMETER SuppressDuplicateDetection Added to every request: do not run duplicate detection. .PARAMETER Tag Added to every request: value shared with the plug-ins. .OUTPUTS Microsoft.Xrm.Sdk.OrganizationResponse. With ReturnResponses, one response per request, in request order ($null for a faulted request). .EXAMPLE $responses = Invoke-XrmBulkRequests -Requests $requests -ReturnResponses $true; .EXAMPLE Invoke-XrmBulkRequests -Requests $requests -ContinueOnError $true -ErrorVariable bulkErrors -ErrorAction SilentlyContinue; $faults = $bulkErrors | ForEach-Object { $_.TargetObject }; $faults | Format-Table Index, RequestName, Message; .LINK https://github.com/AymericM78/PowerDataOps/blob/main/documentation/commands/Invoke-XrmBulkRequests.md #> function Invoke-XrmBulkRequests { [CmdletBinding(SupportsShouldProcess)] [OutputType([Microsoft.Xrm.Sdk.OrganizationResponse])] param ( [Parameter(Mandatory = $false, ValueFromPipeline)] [Microsoft.PowerPlatform.Dataverse.Client.ServiceClient] $XrmClient = $Global:XrmClient, [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [Microsoft.Xrm.Sdk.OrganizationRequest[]] $Requests, [Parameter(Mandatory = $false)] [ValidateRange(1, 1000)] [int] $BatchSize = 500, [Parameter(Mandatory = $false)] [bool] $ContinueOnError = $false, [Parameter(Mandatory = $false)] [bool] $ReturnResponses = $false, [Parameter(Mandatory = $false)] [switch] $Quiet = $false, [Parameter(Mandatory = $false)] [switch] $BypassCustomPluginExecution, [Parameter(Mandatory = $false)] [ValidateSet("CustomSync", "CustomAsync")] [string[]] $BypassBusinessLogicExecution, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Guid[]] $BypassBusinessLogicExecutionStepIds, [Parameter(Mandatory = $false)] [switch] $SuppressCallbackRegistrationExpanderJob, [Parameter(Mandatory = $false)] [switch] $SuppressDuplicateDetection, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string] $Tag ) begin { $StopWatch = [System.Diagnostics.Stopwatch]::StartNew(); Trace-XrmFunction -Name $MyInvocation.MyCommand.Name -Stage Start -Parameters ($MyInvocation.MyCommand.Parameters); } process { [System.Collections.ArrayList] $responses = @(); if (-Not $Requests) { return $responses; } $options = Get-XrmRequestOptionsInternal -BoundParameters $PSBoundParameters; if ($options.Count -gt 0) { foreach ($request in $Requests) { $request | Set-XrmRequestOptions @options | Out-Null; } } $faults = [System.Collections.Generic.List[PSCustomObject]]::new(); $total = $Requests.Count; for ($start = 0; $start -lt $total; $start += $BatchSize) { $end = [Math]::Min($start + $BatchSize, $total) - 1; $batch = [Microsoft.Xrm.Sdk.OrganizationRequest[]]($Requests[$start..$end]); if (-not $Quiet) { Write-HostAndLog -Message " > Processing requests $($start + 1) to $($end + 1) of $total."; } Write-Progress -Activity "Processing requests" -Status "$($end + 1) / $total" -PercentComplete ((($end + 1) * 100) / $total) -Id 1051; try { $batchResponse = Invoke-XrmBulkRequest -XrmClient $XrmClient -Requests $batch -ContinueOnError $ContinueOnError -ReturnResponses $ReturnResponses; } catch { if (-not $ContinueOnError) { Write-Progress -Activity "Processing requests" -Id 1051 -Completed; throw "Batch of requests #$start to #$end failed: $($_.Exception.Message)"; } $faults.Add([PSCustomObject]@{ Index = $start; Count = $batch.Count; RequestName = $null; Message = $_.Exception.Message; }); if ($ReturnResponses) { $responses.AddRange([object[]]::new($batch.Count)); } continue; } $batchResults = [object[]]::new($batch.Count); foreach ($item in $batchResponse.Responses) { if ($null -eq $item.Fault) { $batchResults[$item.RequestIndex] = $item.Response; continue; } $fault = [PSCustomObject]@{ Index = $start + $item.RequestIndex; Count = 1; RequestName = $batch[$item.RequestIndex].RequestName; Message = $item.Fault.Message; }; if (-not $ContinueOnError) { Write-Progress -Activity "Processing requests" -Id 1051 -Completed; throw "Request #$($fault.Index) ($($fault.RequestName)) failed: $($fault.Message)"; } $faults.Add($fault); } if ($ReturnResponses) { $responses.AddRange($batchResults); } } Write-Progress -Activity "Processing requests" -Id 1051 -Completed; $responses; if ($faults.Count -gt 0) { $faultedCount = 0; foreach ($fault in $faults) { $faultedCount += $fault.Count; } $details = ($faults | Select-Object -First 5 | ForEach-Object { "#$($_.Index) ($($_.RequestName)): $($_.Message)" }) -join " | "; Write-Error -Message "$faultedCount of $total requests failed. $details" -TargetObject $faults.ToArray() -Category InvalidResult; } } end { $StopWatch.Stop(); Trace-XrmFunction -Name $MyInvocation.MyCommand.Name -Stage Stop -StopWatch $StopWatch; } } Export-ModuleMember -Function Invoke-XrmBulkRequests -Alias *; |