|
| 1 | +--- |
| 2 | +title: "Web API CDSWebApiService Async Parallel Operations Sample (C#) (Common Data Service)| Microsoft Docs" |
| 3 | +description: "This sample demonstrates using Task Parallel Library (TPL) dataflow components with asynchronous requests." |
| 4 | +ms.custom: "" |
| 5 | +ms.date: 04/20/2020 |
| 6 | +ms.service: powerapps |
| 7 | +applies_to: |
| 8 | + - "Dynamics 365 (online)" |
| 9 | +author: "JimDaly" |
| 10 | +ms.author: "pehecke" |
| 11 | +ms.reviewer: "pehecke" |
| 12 | +search.audienceType: |
| 13 | + - developer |
| 14 | +search.app: |
| 15 | + - PowerApps |
| 16 | + - D365CE |
| 17 | +--- |
| 18 | +# Web API CDSWebApiService Async Parallel Operations Sample (C#) |
| 19 | + |
| 20 | +This sample demonstrates using Task Parallel Library (TPL) dataflow components [Dataflow (Task Parallel Library)](/dotnet/standard/parallel-programming/dataflow-task-parallel-library) with asynchronous requests. |
| 21 | + |
| 22 | +TPL provides capabilities to add parallelism and concurrency to applications. These capabilities are an important part of maximizing throughput when performing operations that will add or update data within CDS. |
| 23 | + |
| 24 | +This sample uses the CDSWebApiService class asynchronous methods within asynchronous operations. Because the CDSWebApiService class can manage Service Protection API limits, this code can be resilient to the transient 429 errors that clients should expect. It will retry a configurable number of times. More information: [Service Protection API Limits](../../api-limits.md) |
| 25 | + |
| 26 | +This sample simply creates a configurable number of account records to create, which it will in turn delete. This sample uses dataflow components to process the records and transform the results of the create operation into the next phase that deletes these records. Because of the nature of this data flow, delete operations for previously created records will start before all the records to create are finished. |
| 27 | + |
| 28 | +## Prerequisites |
| 29 | + |
| 30 | +The following is required to build and run the CDSWebApiService C# samples : |
| 31 | + |
| 32 | +- Microsoft Visual Studio 2019. |
| 33 | +- Access to Common Data Service with privileges to perform CRUD operations. |
| 34 | + |
| 35 | +<a name="bkmk_runSample"></a> |
| 36 | + |
| 37 | +## How to run this sample |
| 38 | + |
| 39 | +1. Go to [Web API CDSWebApiService Sample](https://github.com/microsoft/PowerApps-Samples/tree/master/cds/webapi/C%23/CDSWebApiService), clone or download the samples repository, and extract its contents into a local folder. |
| 40 | + |
| 41 | +1. Open the [CDSWebApiService.sln](https://github.com/microsoft/PowerApps-Samples/blob/master/cds/webapi/C%23/CDSWebApiService/CDSWebApiService.sln). |
| 42 | + |
| 43 | +1. Select the **AsyncParallelOperations** project and open the App.config. This is a common [App.config](https://github.com/microsoft/PowerApps-Samples/blob/master/cds/webapi/C%23/CDSWebApiService/App.config) file used by all the samples in this solution. Once you edit this, you can run any of the samples in this solution. |
| 44 | + |
| 45 | +1. You must edit the `Url`, `UserPrincipalName`, and `Password` values to set the CDS instance and credentials you want to connect to. |
| 46 | + |
| 47 | + ```xml |
| 48 | + <add name="Connect" |
| 49 | + connectionString="Url=https://yourorg.api.crm.dynamics.com; |
| 50 | + Authority=null; |
| 51 | + ClientId=51f81489-12ee-4a9e-aaae-a2591f45987d; |
| 52 | + RedirectUrl=app://58145B91-0C36-4500-8554-080854F2AC97; |
| 53 | + |
| 54 | + Password=y0urp455w0rd; |
| 55 | + CallerObjectId=null; |
| 56 | + Version=9.1; |
| 57 | + MaxRetries=3; |
| 58 | + TimeoutInSeconds=180; |
| 59 | + "/> |
| 60 | + ``` |
| 61 | + |
| 62 | +1. Make sure that the **AsyncParallelOperations** project is set as the startup project. The name of the project should be bold to indicate it is the startup project. If the name is not bold, right-click it in the solution explorer and select **Set as Startup Project**. |
| 63 | + |
| 64 | +1. Press F5 to run the program in debug mode. |
| 65 | + |
| 66 | +## Code listing |
| 67 | + |
| 68 | +This sample depends on the assembly included in the CDSWebAPIService project. For information on the methods this class provides see: [Web API CDSWebApiService class Sample (C#)](cdswebapiservice.md). |
| 69 | + |
| 70 | +The following is the code from the Program.cs file: |
| 71 | + |
| 72 | +```csharp |
| 73 | +using Newtonsoft.Json.Linq; |
| 74 | +using System; |
| 75 | +using System.Collections.Generic; |
| 76 | +using System.Configuration; |
| 77 | +using System.Threading.Tasks; |
| 78 | +using System.Threading.Tasks.Dataflow; |
| 79 | + |
| 80 | +namespace PowerApps.Samples |
| 81 | +{ |
| 82 | + internal class Program |
| 83 | + { |
| 84 | + //Get configuration data from App.config connectionStrings |
| 85 | + private static readonly string connectionString = ConfigurationManager.ConnectionStrings["Connect"].ConnectionString; |
| 86 | + |
| 87 | + private static readonly ServiceConfig serviceConfig = new ServiceConfig(connectionString); |
| 88 | + |
| 89 | + //Controls the max degree of parallelism |
| 90 | + private static readonly int maxDegreeOfParallelism = 10; |
| 91 | + |
| 92 | + //How many records to create with this sample. |
| 93 | + private static readonly int numberOfRecords = 100; |
| 94 | + |
| 95 | + private static async Task Main() |
| 96 | + { |
| 97 | + #region Optimize Connection |
| 98 | + |
| 99 | + //Change max connections from .NET to a remote service default: 2 |
| 100 | + System.Net.ServicePointManager.DefaultConnectionLimit = 65000; |
| 101 | + //Bump up the min threads reserved for this app to ramp connections faster - minWorkerThreads defaults to 4, minIOCP defaults to 4 |
| 102 | + System.Threading.ThreadPool.SetMinThreads(100, 100); |
| 103 | + //Turn off the Expect 100 to continue message - 'true' will cause the caller to wait until it round-trip confirms a connection to the server |
| 104 | + System.Net.ServicePointManager.Expect100Continue = false; |
| 105 | + //Can decrease overall transmission overhead but can cause delay in data packet arrival |
| 106 | + System.Net.ServicePointManager.UseNagleAlgorithm = false; |
| 107 | + |
| 108 | + #endregion Optimize Connection |
| 109 | + |
| 110 | + var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions |
| 111 | + { |
| 112 | + MaxDegreeOfParallelism = maxDegreeOfParallelism |
| 113 | + }; |
| 114 | + |
| 115 | + var count = 0; |
| 116 | + double secondsToComplete; |
| 117 | + |
| 118 | + //Will be populated with account records to import |
| 119 | + List<JObject> accountsToImport = new List<JObject>(); |
| 120 | + |
| 121 | + Console.WriteLine($"Preparing to create {numberOfRecords} acccount records using Web API."); |
| 122 | + |
| 123 | + //Add account records to the list to import |
| 124 | + while (count < numberOfRecords) |
| 125 | + { |
| 126 | + var account = new JObject |
| 127 | + { |
| 128 | + ["name"] = $"Account {count}" |
| 129 | + }; |
| 130 | + accountsToImport.Add(account); |
| 131 | + count++; |
| 132 | + } |
| 133 | + |
| 134 | + using (var svc = new CDSWebApiService(serviceConfig)) |
| 135 | + { |
| 136 | + secondsToComplete = await ProcessData(svc, accountsToImport, executionDataflowBlockOptions); |
| 137 | + } |
| 138 | + |
| 139 | + Console.WriteLine($"Created and deleted {accountsToImport.Count} accounts in {Math.Round(secondsToComplete)} seconds."); |
| 140 | + |
| 141 | + Console.WriteLine("Sample completed. Press any key to exit."); |
| 142 | + Console.ReadLine(); |
| 143 | + } |
| 144 | + |
| 145 | + private static async Task<double> ProcessData(CDSWebApiService svc, List<JObject> accountsToImport, |
| 146 | + ExecutionDataflowBlockOptions executionDataflowBlockOptions) |
| 147 | + { |
| 148 | + var createAccounts = new TransformBlock<JObject, Uri>( |
| 149 | + async a => |
| 150 | + { |
| 151 | + return await svc.PostCreateAsync("accounts", a); |
| 152 | + }, |
| 153 | + executionDataflowBlockOptions |
| 154 | + ); |
| 155 | + |
| 156 | + var deleteAccounts = new ActionBlock<Uri>( |
| 157 | + async u => |
| 158 | + { |
| 159 | + await svc.DeleteAsync(u); |
| 160 | + }, |
| 161 | + executionDataflowBlockOptions |
| 162 | + ); |
| 163 | + |
| 164 | + createAccounts.LinkTo(deleteAccounts, new DataflowLinkOptions { PropagateCompletion = true }); |
| 165 | + |
| 166 | + var start = DateTime.Now; |
| 167 | + |
| 168 | + accountsToImport.ForEach(a => createAccounts.SendAsync(a)); |
| 169 | + createAccounts.Complete(); |
| 170 | + await deleteAccounts.Completion; |
| 171 | + |
| 172 | + //Calculate the duration to complete |
| 173 | + return (DateTime.Now - start).TotalSeconds; |
| 174 | + } |
| 175 | + } |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +### See also |
| 180 | + |
| 181 | +[Use the Common Data Service Web API](../overview.md)<br /> |
| 182 | +[Web API CDSWebApiService class Sample (C#)](cdswebapiservice.md)<br /> |
| 183 | +[Web API CDSWebApiService Basic Operations Sample (C#)](cdswebapiservice-basic-operations.md)<br /> |
| 184 | +[Web API CDSWebApiService Parallel Operations Sample (C#)](cdswebapiservice-parallel-operations.md)<br /> |
| 185 | +[Create an entity using the Web API](../create-entity-web-api.md)<br /> |
| 186 | +[Update and delete entities using the Web API](../update-delete-entities-using-web-api.md) |
0 commit comments