Skip to content

Commit 2a560e0

Browse files
authored
Merge pull request MicrosoftDocs#3229 from MicrosoftDocs/pehecke-webapi-sample-update
Updated topic with latest changes to the sample
2 parents 22156d9 + 439eaf5 commit 2a560e0

File tree

1 file changed

+41
-79
lines changed

1 file changed

+41
-79
lines changed

powerapps-docs/developer/common-data-service/webapi/samples/functions-actions-csharp.md

Lines changed: 41 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: "Web API Functions and Actions Sample (C#) (Common Data Service)| Microsoft Docs"
33
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#"
44
ms.custom: ""
5-
ms.date: 1/09/2019
5+
ms.date: 8/06/2020
66
ms.service: powerapps
77
ms.suite: ""
88
ms.tgt_pltfrm: ""
@@ -49,12 +49,14 @@ Go to [Web API Functions and Actions Sample (C#)](https://github.com/Microsoft/P
4949
|WebAPIFunctionsandActions_1_0_0_0_managed.zip|A custom managed solution containing two custom actions called by this sample.|
5050

5151
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.
5254

5355
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.
5456

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.
5658

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.
5860

5961
<a name="bkmk_codeListing"></a>
6062

@@ -63,8 +65,7 @@ Next, use the following procedure to run this sample.
6365
`SampleProgram.cs`
6466

6567
```csharp
66-
67-
using Newtonsoft.Json.Linq;
68+
using Newtonsoft.Json.Linq;
6869
using System;
6970
using System.Collections.Generic;
7071
using System.Configuration;
@@ -78,6 +79,13 @@ using System.Threading.Tasks;
7879
namespace PowerApps.Samples
7980

8081
{
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"/>
8189
public partial class SampleProgram
8290
{
8391
static void Main(string[] args)
@@ -94,9 +102,10 @@ namespace PowerApps.Samples
94102
"v9.0"))
95103
{
96104
CreateRequiredRecords(client);
97-
//DeleteRequiredRecords(client, prompt);
105+
98106
HttpRequestMessage request;
99107
HttpResponseMessage response;
108+
100109
#region Call an unbound function with no parameters.
101110
//Retrieve the current user's full name from the WhoAmI function:
102111
// https://msdn.microsoft.com/library/mt607925.aspx, which returns a WhoAmIResponse
@@ -111,6 +120,8 @@ namespace PowerApps.Samples
111120
throw new Exception(string.Format("Failed to retrieve current user", response.Content));
112121
}
113122
JObject whoAmIresp = JObject.Parse(response.Content.ReadAsStringAsync().Result);
123+
response.Dispose();
124+
114125
//First obtain the user's ID.
115126
myUserId = (Guid)whoAmIresp["UserId"];
116127
//Then retrieve the full name for that unique ID.
@@ -128,6 +139,8 @@ namespace PowerApps.Samples
128139
Console.WriteLine("Error calling WhoAmI!");
129140
throw new Exception(string.Format("Failed to retrieve the fullname for that unique ID", response.Content));
130141
}
142+
response.Dispose();
143+
131144
Console.WriteLine("\tCurrent user has system name '{0}'.", currentUser);
132145
#endregion Call an unbound function with no parameters.
133146

@@ -161,6 +174,8 @@ namespace PowerApps.Samples
161174
throw new Exception(string.Format("Faile calling GetTimeZoneCodeByLocalizedName!", response.Content));
162175

163176
}
177+
response.Dispose();
178+
164179
timeZoneCode = LocalizedNameResponse["TimeZoneCode"].ToString();
165180
Console.WriteLine("\tThe time zone '{0}' has the code '{1}'.", timeZoneName, timeZoneCode);
166181
#endregion Call an unbound function that requires parameters.
@@ -185,6 +200,8 @@ namespace PowerApps.Samples
185200
Console.WriteLine("Error calling CalculateTotalTimeIncident!");
186201
throw new Exception(string.Format("Failed calling CalculateTotalTimeIncident!", response.Content));
187202
}
203+
response.Dispose();
204+
188205
Console.WriteLine("\tThe total duration of tasks associated with the incident " +
189206
"is {0} minutes.", totalTime);
190207
#endregion Call a bound function.
@@ -214,19 +231,22 @@ namespace PowerApps.Samples
214231
Console.WriteLine("Error calling WinOpportunity!");
215232
throw new Exception(string.Format("Failed to close an opportunity as won", response.Content));
216233
}
234+
response.Dispose();
235+
217236
Console.WriteLine("\tOpportunity won.");
218237
#endregion Call an unbound action that requires parameters.
219238

220239
#region Call a bound action that requires parameters.
221240
//Add a new letter tracking activity to the current user's queue. Uses the AddToQueue
222241
//action: https://msdn.microsoft.com/library/mt607880.aspx, which is bound to the queue
223242
//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.
225244
string queueItemId;
226245
//Create a letter tracking instance.
227246
string letterUri;
228247
JObject letter = new JObject();
229-
letter["description"] = "Example letter";
248+
letter["subject"] = "Example letter";
249+
letter["description"] = "Body of the letter";
230250
Console.WriteLine("Bound action: AddToQueue");
231251
request = new HttpRequestMessage(HttpMethod.Post, client.BaseAddress + "letters");
232252
request.Content = new StringContent(letter.ToString(), Encoding.UTF8, "application/json");
@@ -242,10 +262,11 @@ namespace PowerApps.Samples
242262
Console.WriteLine("Error creating tracking letter!");
243263
throw new Exception(string.Format("Failed to create a Letter", response.Content));
244264
}
265+
response.Dispose();
245266

246267
//Retrieve the ID associated with this new letter tracking activity.
247268
string letterActivityId;
248-
response = client.GetAsync(letterUri + "?$select=activityid",
269+
response = client.GetAsync(letterUri + "?$select=activityid,subject",
249270
HttpCompletionOption.ResponseHeadersRead).Result;
250271
if (response.IsSuccessStatusCode)
251272
{
@@ -257,6 +278,7 @@ namespace PowerApps.Samples
257278
Console.WriteLine("Error retrieving tracking letter activity ID!");
258279
throw new Exception(string.Format("Failed to retrieve trscking letter activity ID", response.Content));
259280
}
281+
response.Dispose();
260282

261283
//Retrieve URL to current user's queue.
262284
string myQueueUri;
@@ -272,13 +294,14 @@ namespace PowerApps.Samples
272294
Console.WriteLine("Error retrieving current user queue URL!");
273295
throw new Exception(string.Format("Failed to retrieve URL to current user's queue", response.Content));
274296
}
297+
response.Dispose();
275298

276299
//Add letter activity to current user's queue, then return its queue ID.
277300
JObject addToQueueParams = new JObject();
278301
addToQueueParams["Target"] = JObject.Parse(
279302
@"{activityid: '" + letterActivityId + @"', '@odata.type': 'Microsoft.Dynamics.CRM.letter' }");
280303
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");
282305
response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
283306
if (response.StatusCode == HttpStatusCode.OK)
284307
{
@@ -290,76 +313,11 @@ namespace PowerApps.Samples
290313
Console.WriteLine("Error adding letter activity to queue!");
291314
throw new Exception(string.Format("Failed to Add letter activity to current user's queue", response.Content));
292315
}
316+
response.Dispose();
317+
293318
Console.WriteLine("\tQueueItemId returned from AddToQueue action: {0}", queueItemId);
294319
#endregion Call a bound action that requires parameters.
295320

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-
363321
#region Call a bound custom action that requires parameters.
364322
//Add a note to a specified contact. Uses the custom action sample_AddNoteToContact, which
365323
//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
379337
}
380338
else
381339
{
382-
Console.WriteLine("Error calling custom action sample_AddNoteToContact!");
340+
Console.WriteLine("Error calling custom action AddNoteToContact!");
383341
throw new Exception(string.Format("Failed calling custom action sample_AddNoteToContact", response.Content));
384342
}
343+
response.Dispose();
344+
385345
Console.WriteLine("\tA note with the title '{0}' was created and " +
386346
"associated with the contact {2}.",
387347
note1["NoteTitle"], note1["NoteText"],
@@ -404,6 +364,8 @@ namespace PowerApps.Samples
404364
Console.WriteLine("Error calling custom action sample_CreateCustomer!");
405365
throw new Exception(string.Format("Failed calling custom action sample_CreateCustomer", response.Content));
406366
}
367+
response.Dispose();
368+
407369
Console.WriteLine("\tThe account '" + customerName1 + "' was created.");
408370

409371
//Try to call the same custom action with invalid parameters, here the same name is
@@ -420,6 +382,7 @@ namespace PowerApps.Samples
420382
Exception ex = new Exception(string.Format("Failed calling custom action", response.Content));
421383
Console.WriteLine("\tExpected Error: " + ex.Message);
422384
}
385+
response.Dispose();
423386
#endregion Call an unbound custom action that requires parameters.
424387

425388
DeleteRequiredRecords(client, prompt);
@@ -438,7 +401,6 @@ namespace PowerApps.Samples
438401
}
439402
}
440403
}
441-
442404
```
443405

444406
### See also

0 commit comments

Comments
 (0)