How to decrease the execution time of multiple requests in Dynamics 365

1.6k views Asked by At

I'm in big trouble. The execution time of my multiple requests in the code is being longer than the azure app timeout. I need to update a lot of records and in the end return some data to the web site in azure. I'm sending batches of 200 requests to update 200 records. I need a faster way to batch update records.

My code:

public static Boolean BulkUpdateNoSorteado(CrmServiceClient service, EntityCollection entities)
{
    // Create an ExecuteMultipleRequest object.
    var multipleRequest = new ExecuteMultipleRequest()
            {
                // Assign settings that define execution behavior: continue on error, return responses. 
                Settings = new ExecuteMultipleSettings()
                {
                    ContinueOnError = false,
                    ReturnResponses = true
                },

                // Create an empty organization request collection.
                Requests = new OrganizationRequestCollection()
            };

    try
    {
        var countRequest = Int32.Parse(ConfigurationManager.AppSettings["requestCount"]);
                
        // Add a UpdateRequest for each entity to the request collection.
        foreach (var entity in entities.Entities)
        {
            SetStateRequest request = new SetStateRequest
                    {
                        EntityMoniker = new EntityReference(entity.LogicalName, entity.Id),
                        State = new OptionSetValue(1),
                        Status = new OptionSetValue((int)Domain.Enum.EnumStatusTicket.Nao_sorteado)
                    };
            multipleRequest.Requests.Add(request);

            if (multipleRequest.Requests.Count == countRequest || entity == entities.Entities.Last())
            {
                if (service.OrganizationServiceProxy == null)
                {
                    service = FactoryGetService.AccessTokenGeneratorAsync();
                }

                ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);

                multipleRequest = new ExecuteMultipleRequest()
                        {
                            // Assign settings that define execution behavior: continue on error, return responses. 
                            Settings = new ExecuteMultipleSettings()
                            {
                                ContinueOnError = false,
                                ReturnResponses = true
                            },
                            // Create an empty organization request collection.
                            Requests = new OrganizationRequestCollection()
                        };
                }
       }

       return true;
    } 
    catch (Exception)
    {
         throw;
    }
}
1

There are 1 answers

1
Halim Saad-Rached On BEST ANSWER

Try to refactor your code to use:

  1. Parallel programming (like Parallel.ForEach, Parallel.For in C# for example or,
  2. Pure multi-threading hence letting multiple worker threads cooperate on the multiple updates at the same time. In that regard you can share the EntityCollection in a concurrent queue (ConcurrentQueue<T> in C# for example).
    You can test easier for Azure timeout if you configure the number of threads and the batch size of the multiple updates in your solution.
  3. As much as possible CRM organization URLs for the threads.

Here's what the code in 2. might look like:

using System;
using System.Threading;
using System.Data;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Messages;

public class EntitiesPage : List<Entity> { }
    
public class MultiThreadedDynamicsCrmUpdate
{        
    //EntityCollection ec = ...;
    //CrmServiceClient service = FactoryGetService.AccessTokenGeneratorAsync();
    //int statusOptionSetValue = (int)Domain.Enum.EnumStatusTicket.Nao_sorteado;
        
    int updatesPageSize = 200;
    int threadsNumber = 10;
    ConcurrentQueue<EntitiesPage> cq = new ConcurrentQueue<EntitiesPage>();
        
    public static void Main(string[] args)
    {
        UpdateAll();
    }

    public static void UpdateAll()
    {
        PaginateEntities(ec);

        List<Thread> threads = new List<Thread>();
        for (int i = 0; i < threadsNumber; i++)
        {
            threads.Add(new Thread(UpdateACrmEntitiesPageByThread));
        }

        foreach (Thread thread in threads)
        {
            thread.Start();
        }

        foreach (Thread thread in threads)
        {
            thread.Join();
        }
    }
    
    public static void PaginateEntities(EntityCollection ec)
    {
        EntitiesPage page = new EntitiesPage();
        
        foreach (Entity e in ec.Entities)
        {
            page.Add(e);
            if (page.Count == updatesPageSize)
            {
                cq.Enqueue(page);
                page = new EntitiesPage();
            }
        }
        cq.Enqueue(page);
    }

    public static void UpdateACrmEntitiesPageByThread()
    {
        EntitiesPage page = new EntitiesPage();

        while (!cq.IsEmpty)
        { 
            if (cq.TryDequeue(out page))
            {
                var multipleRequest = new ExecuteMultipleRequest()
                {
                    Settings = new ExecuteMultipleSettings()
                    {
                        ContinueOnError = false,
                        ReturnResponses = true
                    },
                    Requests = new OrganizationRequestCollection()
                };

                foreach (Entity e in page)
                {
                    SetStateRequest request = new SetStateRequest
                    {
                        EntityMoniker = new EntityReference(e.LogicalName, e.Id),
                        State = new OptionSetValue(1),
                        Status = new OptionSetValue(...)
                    };
                    multipleRequest.Requests.Add(request);
                }

                ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);
            }
        }
    }
}