2
2
title : " Web API Functions and Actions Sample (C#) (Common Data Service)| Microsoft Docs"
3
3
description : " This sample demonstrates how to call bound and unbound functions and actions, including custom actions, using the Common Data Service Web API and C#"
4
4
ms.custom : " "
5
- ms.date : 1/09/2019
5
+ ms.date : 8/06/2020
6
6
ms.service : powerapps
7
7
ms.suite : " "
8
8
ms.tgt_pltfrm : " "
@@ -49,12 +49,14 @@ Go to [Web API Functions and Actions Sample (C#)](https://github.com/Microsoft/P
49
49
| WebAPIFunctionsandActions_1_0_0_0_managed.zip| A custom managed solution containing two custom actions called by this sample.|
50
50
51
51
Next, use the following procedure to run this sample.
52
+
53
+ 1 . Using Dynamics 365 Customer Engagement (** Settings** > ** Solutions** ) or Power Apps (** Solutions** ), import the provided solution (.zip) file into your testing environment.
52
54
53
55
1 . Locate and double-click on the solution file, FunctionsAndActions.sln, to load the solution into Visual Studio. Build the ** FunctionsAndActions** solution. This should automatically download and install all the required NuGet packages that are either missing or need to be updated.
54
56
55
- 2 . Edit the application configuration file, App.config, to specify connection information for your Common Data Service server.
57
+ 1 . Edit the application configuration file, App.config, to specify connection information for your Common Data Service server.
56
58
57
- 3 . Run the ** FunctionsAndActions** project from within Visual Studio. All sample solutions are configured to run in debug mode by default.
59
+ 1 . Build and run the ** FunctionsAndActions** project from within Visual Studio. All sample solutions are configured to run in debug mode by default.
58
60
59
61
<a name =" bkmk_codeListing " ></a >
60
62
@@ -63,8 +65,7 @@ Next, use the following procedure to run this sample.
63
65
` SampleProgram.cs `
64
66
65
67
``` csharp
66
-
67
- using Newtonsoft .Json .Linq ;
68
+ using Newtonsoft .Json .Linq ;
68
69
using System ;
69
70
using System .Collections .Generic ;
70
71
using System .Configuration ;
@@ -78,6 +79,13 @@ using System.Threading.Tasks;
78
79
namespace PowerApps .Samples
79
80
80
81
{
82
+ // TODO The reader must load the provided sample solution into the target testing environment
83
+ // Otherwise, the custom action code in this program will not work.
84
+
85
+ /// <summary >
86
+ /// Demonstrates Web API functions and actions.
87
+ /// </summary >
88
+ /// <seealso cref =" https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/webapi/samples/functions-actions-csharp" />
81
89
public partial class SampleProgram
82
90
{
83
91
static void Main (string [] args )
@@ -94,9 +102,10 @@ namespace PowerApps.Samples
94
102
" v9.0" ))
95
103
{
96
104
CreateRequiredRecords (client );
97
- // DeleteRequiredRecords(client, prompt);
105
+
98
106
HttpRequestMessage request ;
99
107
HttpResponseMessage response ;
108
+
100
109
#region Call an unbound function with no parameters.
101
110
// Retrieve the current user's full name from the WhoAmI function:
102
111
// https://msdn.microsoft.com/library/mt607925.aspx, which returns a WhoAmIResponse
@@ -111,6 +120,8 @@ namespace PowerApps.Samples
111
120
throw new Exception (string .Format (" Failed to retrieve current user" , response .Content ));
112
121
}
113
122
JObject whoAmIresp = JObject .Parse (response .Content .ReadAsStringAsync ().Result );
123
+ response .Dispose ();
124
+
114
125
// First obtain the user's ID.
115
126
myUserId = (Guid )whoAmIresp [" UserId" ];
116
127
// Then retrieve the full name for that unique ID.
@@ -128,6 +139,8 @@ namespace PowerApps.Samples
128
139
Console .WriteLine (" Error calling WhoAmI!" );
129
140
throw new Exception (string .Format (" Failed to retrieve the fullname for that unique ID" , response .Content ));
130
141
}
142
+ response .Dispose ();
143
+
131
144
Console .WriteLine (" \t Current user has system name '{0}'." , currentUser );
132
145
#endregion Call an unbound function with no parameters.
133
146
@@ -161,6 +174,8 @@ namespace PowerApps.Samples
161
174
throw new Exception (string .Format (" Faile calling GetTimeZoneCodeByLocalizedName!" , response .Content ));
162
175
163
176
}
177
+ response .Dispose ();
178
+
164
179
timeZoneCode = LocalizedNameResponse [" TimeZoneCode" ].ToString ();
165
180
Console .WriteLine (" \t The time zone '{0}' has the code '{1}'." , timeZoneName , timeZoneCode );
166
181
#endregion Call an unbound function that requires parameters.
@@ -185,6 +200,8 @@ namespace PowerApps.Samples
185
200
Console .WriteLine (" Error calling CalculateTotalTimeIncident!" );
186
201
throw new Exception (string .Format (" Failed calling CalculateTotalTimeIncident!" , response .Content ));
187
202
}
203
+ response .Dispose ();
204
+
188
205
Console .WriteLine (" \t The total duration of tasks associated with the incident " +
189
206
" is {0} minutes." , totalTime );
190
207
#endregion Call a bound function.
@@ -214,19 +231,22 @@ namespace PowerApps.Samples
214
231
Console .WriteLine (" Error calling WinOpportunity!" );
215
232
throw new Exception (string .Format (" Failed to close an opportunity as won" , response .Content ));
216
233
}
234
+ response .Dispose ();
235
+
217
236
Console .WriteLine (" \t Opportunity won." );
218
237
#endregion Call an unbound action that requires parameters.
219
238
220
239
#region Call a bound action that requires parameters.
221
240
// Add a new letter tracking activity to the current user's queue. Uses the AddToQueue
222
241
// action: https://msdn.microsoft.com/library/mt607880.aspx, which is bound to the queue
223
242
// entity type: https://msdn.microsoft.com/library/mt607886.aspx, and returns a
224
- // AddToQueueResponse complex type: https://msdn.microsoft.com/library/mt608105.aspx.
243
+ // AddToQueueResponse complex type: https://msdn.microsoft.com/en-us/ library/mt608105.aspx.
225
244
string queueItemId ;
226
245
// Create a letter tracking instance.
227
246
string letterUri ;
228
247
JObject letter = new JObject ();
229
- letter [" description" ] = " Example letter" ;
248
+ letter [" subject" ] = " Example letter" ;
249
+ letter [" description" ] = " Body of the letter" ;
230
250
Console .WriteLine (" Bound action: AddToQueue" );
231
251
request = new HttpRequestMessage (HttpMethod .Post , client .BaseAddress + " letters" );
232
252
request .Content = new StringContent (letter .ToString (), Encoding .UTF8 , " application/json" );
@@ -242,10 +262,11 @@ namespace PowerApps.Samples
242
262
Console .WriteLine (" Error creating tracking letter!" );
243
263
throw new Exception (string .Format (" Failed to create a Letter" , response .Content ));
244
264
}
265
+ response .Dispose ();
245
266
246
267
// Retrieve the ID associated with this new letter tracking activity.
247
268
string letterActivityId ;
248
- response = client .GetAsync (letterUri + " ?$select=activityid" ,
269
+ response = client .GetAsync (letterUri + " ?$select=activityid,subject " ,
249
270
HttpCompletionOption .ResponseHeadersRead ).Result ;
250
271
if (response .IsSuccessStatusCode )
251
272
{
@@ -257,6 +278,7 @@ namespace PowerApps.Samples
257
278
Console .WriteLine (" Error retrieving tracking letter activity ID!" );
258
279
throw new Exception (string .Format (" Failed to retrieve trscking letter activity ID" , response .Content ));
259
280
}
281
+ response .Dispose ();
260
282
261
283
// Retrieve URL to current user's queue.
262
284
string myQueueUri ;
@@ -272,13 +294,14 @@ namespace PowerApps.Samples
272
294
Console .WriteLine (" Error retrieving current user queue URL!" );
273
295
throw new Exception (string .Format (" Failed to retrieve URL to current user's queue" , response .Content ));
274
296
}
297
+ response .Dispose ();
275
298
276
299
// Add letter activity to current user's queue, then return its queue ID.
277
300
JObject addToQueueParams = new JObject ();
278
301
addToQueueParams [" Target" ] = JObject .Parse (
279
302
@" {activityid: '" + letterActivityId + @" ', '@odata.type': 'Microsoft.Dynamics.CRM.letter' }" );
280
303
request = new HttpRequestMessage (HttpMethod .Post , myQueueUri + " /Microsoft.Dynamics.CRM.AddToQueue" );
281
- request .Content = new StringContent (addToQueueParams .ToString ());
304
+ request .Content = new StringContent (addToQueueParams .ToString (), Encoding . UTF8 , " application/json " );
282
305
response = client .SendAsync (request , HttpCompletionOption .ResponseHeadersRead ).Result ;
283
306
if (response .StatusCode == HttpStatusCode .OK )
284
307
{
@@ -290,76 +313,11 @@ namespace PowerApps.Samples
290
313
Console .WriteLine (" Error adding letter activity to queue!" );
291
314
throw new Exception (string .Format (" Failed to Add letter activity to current user's queue" , response .Content ));
292
315
}
316
+ response .Dispose ();
317
+
293
318
Console .WriteLine (" \t QueueItemId returned from AddToQueue action: {0}" , queueItemId );
294
319
#endregion Call a bound action that requires parameters.
295
320
296
- // Attempt to load the associated managed solution so that we can call its custom actions.
297
- // Check first if the solution is already installed by retrieving its ID.
298
- customSolutionID = customSolutionName ;
299
- bool install = true ;
300
- // Request to install and solution is not already present
301
- if (install == true && customSolutionID == null )
302
- {
303
- // Locate the custom solution zip file, which should have been copied over to the build
304
- // output directory.
305
- string solutionPath = Directory .GetCurrentDirectory () + " \\ " + customSolutionFilename ;
306
- if (! File .Exists (solutionPath ))
307
- { return ; }
308
- // Read the solution package into memory
309
- byte [] packageBytes = File .ReadAllBytes (solutionPath );
310
- // Import the solution package.
311
- JObject importParams = new JObject ();
312
- importParams [" CustomizationFile" ] = packageBytes ;
313
- importParams [" OverwriteUnmanagedCustomizations" ] = false ;
314
- importParams [" PublishWorkflows" ] = false ;
315
- importParams [" ImportJobId" ] = Guid .NewGuid ();
316
-
317
- request = new HttpRequestMessage (HttpMethod .Post , " ImportSolution" );
318
- request .Content = new StringContent (importParams .ToString (), Encoding .UTF8 , " application/json" );
319
- response = client .SendAsync (request , HttpCompletionOption .ResponseContentRead ).Result ;
320
- if (response .IsSuccessStatusCode )
321
- {
322
- customSolutionID = customSolutionName ;
323
- string solutionUri = client .BaseAddress .ToString () + " solutions(" + customSolutionID + " )" ;
324
- entityUris .Add (solutionUri ); // Add to lifetime-managed records.
325
- return ;
326
- }
327
- else
328
- { throw new Exception (string .Format (" Failed to import solution package" , response .Content )); }
329
- }
330
- // Request to uninstall and solution is present.
331
- else if (install == false && customSolutionID != null )
332
- {
333
- string solutionUri = client .BaseAddress .ToString () + " solutions(" + customSolutionID + " )" ;
334
- response = client .DeleteAsync (solutionUri ).Result ;
335
- customSolutionID = null ;
336
- }
337
- if (customSolutionID == null )
338
- {
339
- Console .WriteLine (" Failed to install custom solution, so custom operations cannot be called." );
340
- return ;
341
- }
342
-
343
- string solutionID = null ;
344
-
345
- if (String .IsNullOrEmpty (solutionName ))
346
- { // return null;
347
- }
348
- string queryOptions = " solutions?$select=solutionid&$filter=uniquename eq '" + solutionName + " '" ;
349
- response = client .GetAsync (queryOptions , HttpCompletionOption .ResponseHeadersRead ).Result ;
350
- if (response .StatusCode == HttpStatusCode .OK )
351
- {
352
- JObject solutionArray = JObject .Parse (response .Content .ReadAsStringAsync ().Result );
353
- // There can only be zero or one returned record when filtering on a unique property.
354
- if (! solutionArray [" value" ].Any ())
355
- { solutionID = null ; }
356
- else
357
- { solutionID = solutionArray [" value" ].First ()[" solutionid" ].ToString (); }
358
- }
359
- else
360
- { throw new Exception (string .Format (" Failed to get solutionID" , response .Content )); }
361
- /// return solutionID;
362
-
363
321
#region Call a bound custom action that requires parameters.
364
322
// Add a note to a specified contact. Uses the custom action sample_AddNoteToContact, which
365
323
// is bound to the contact to annotate, and takes a single param, the note to add. It also
@@ -379,9 +337,11 @@ namespace PowerApps.Samples
379
337
}
380
338
else
381
339
{
382
- Console .WriteLine (" Error calling custom action sample_AddNoteToContact !" );
340
+ Console .WriteLine (" Error calling custom action AddNoteToContact !" );
383
341
throw new Exception (string .Format (" Failed calling custom action sample_AddNoteToContact" , response .Content ));
384
342
}
343
+ response .Dispose ();
344
+
385
345
Console .WriteLine (" \t A note with the title '{0}' was created and " +
386
346
" associated with the contact {2}." ,
387
347
note1 [" NoteTitle" ], note1 [" NoteText" ],
@@ -404,6 +364,8 @@ namespace PowerApps.Samples
404
364
Console .WriteLine (" Error calling custom action sample_CreateCustomer!" );
405
365
throw new Exception (string .Format (" Failed calling custom action sample_CreateCustomer" , response .Content ));
406
366
}
367
+ response .Dispose ();
368
+
407
369
Console .WriteLine (" \t The account '" + customerName1 + " ' was created." );
408
370
409
371
// Try to call the same custom action with invalid parameters, here the same name is
@@ -420,6 +382,7 @@ namespace PowerApps.Samples
420
382
Exception ex = new Exception (string .Format (" Failed calling custom action" , response .Content ));
421
383
Console .WriteLine (" \t Expected Error: " + ex .Message );
422
384
}
385
+ response .Dispose ();
423
386
#endregion Call an unbound custom action that requires parameters.
424
387
425
388
DeleteRequiredRecords (client , prompt );
@@ -438,7 +401,6 @@ namespace PowerApps.Samples
438
401
}
439
402
}
440
403
}
441
-
442
404
```
443
405
444
406
### See also
0 commit comments