public static void Main() { Console.Title = "Function and Actions demonstration"; // Track entity instance URIs so those records can be deleted prior to exit. Dictionary <string, Uri> entityUris = new Dictionary <string, Uri>(); try { // Get environment configuration information from the connection string in App.config. ServiceConfig config = new ServiceConfig( ConfigurationManager.ConnectionStrings["Connect"].ConnectionString); // Use the service class that handles HTTP messaging, error handling, and // performance optimizations. using (CDSWebApiService svc = new CDSWebApiService(config)) { // Create any entity instances required by the program code that follows CreateRequiredRecords(svc, entityUris); #region Call an unbound function with no parameters Console.WriteLine("\n* Call an unbound function with no parameters."); // Retrieve the current user's full name from the WhoAmI function: Console.Write("\tGetting information on the current user.."); JToken currentUser = svc.Get("WhoAmI"); // Obtain the user's ID and full name JToken user = svc.Get("systemusers(" + currentUser["UserId"] + ")?$select=fullname"); Console.WriteLine("completed."); Console.WriteLine("\tCurrent user's full name is '{0}'.", user["fullname"]); #endregion Call an unbound function with no parameters #region Call an unbound function that requires parameters Console.WriteLine("\n* Call an unbound function that requires parameters"); // Retrieve the code for the specified time zone int localeID = 1033; string timeZoneName = "Pacific Standard Time"; // Define the unbound function and its parameters string[] uria = new string[] { "GetTimeZoneCodeByLocalizedName", "(LocalizedStandardName=@p1,LocaleId=@p2)", "?@p1='" + timeZoneName + "'&@p2=" + localeID }; // This would also work: // string[] uria = ["GetTimeZoneCodeByLocalizedName", "(LocalizedStandardName='" + // timeZoneName + "',LocaleId=" + localeId + ")"]; JToken localizedName = svc.Get(string.Join("", uria)); string timeZoneCode = localizedName["TimeZoneCode"].ToString(); Console.WriteLine( "\tThe time zone '{0}' has the code '{1}'.", timeZoneName, timeZoneCode); #endregion Call an unbound function that requires parameters #region Call a bound function Console.WriteLine("\n* Call a bound function"); // Retrieve the total time (minutes) spent on all tasks associated with // incident "Sample Case". string boundUri = entityUris["Sample Case"] + @"/Microsoft.Dynamics.CRM.CalculateTotalTimeIncident()"; JToken cttir = svc.Get(boundUri); string totalTime = cttir["TotalTime"].ToString(); Console.WriteLine("\tThe total duration of tasks associated with the incident " + "is {0} minutes.", totalTime); #endregion Call a bound function #region Call an unbound action that requires parameters Console.WriteLine("\n* Call an unbound action that requires parameters"); // Close the existing opportunity "Opportunity to win" and mark it as won. JObject opportClose = new JObject() { { "subject", "Won Opportunity" }, { "*****@*****.**", entityUris["Opportunity to win"] } }; JObject winOpportParams = new JObject() { { "Status", "3" }, { "OpportunityClose", opportClose } }; JObject won = svc.Post("WinOpportunity", winOpportParams); Console.WriteLine("\tOpportunity won."); #endregion Call an unbound action that requires parameters #region Call a bound action that requires parameters Console.WriteLine("\n* Call a bound action that requires parameters"); // Add a new letter tracking activity to the current user's queue. // First create a letter tracking instance. JObject letterAttributes = new JObject() { { "subject", "Example letter" }, { "description", "Body of the letter" } }; Console.Write("\tCreating letter 'Example letter'.."); Uri letterUri = svc.PostCreate("letters", letterAttributes); entityUris.Add("Example letter", letterUri); Console.WriteLine("completed."); //Retrieve the ID associated with this new letter tracking activity. JToken letter = svc.Get(letterUri + "?$select=activityid,subject"); string letterActivityId = (string)letter["activityid"]; // Retrieve the URL to current user's queue. string myUserId = (string)currentUser["UserId"]; JToken queueRef = svc.Get("systemusers(" + myUserId + ")/queueid/$ref"); string myQueueUri = (string)queueRef["@odata.id"]; //Add the letter activity to current user's queue, then return its queue ID. JObject targetUri = JObject.Parse( @"{activityid: '" + letterActivityId + @"', '@odata.type': 'Microsoft.Dynamics.CRM.letter' }"); JObject addToQueueParams = new JObject() { { "Target", targetUri } }; string queueItemId = (string)svc.Post( myQueueUri + "/Microsoft.Dynamics.CRM.AddToQueue", addToQueueParams)["QueueItemId"]; Console.WriteLine("\tLetter 'Example letter' added to current user's queue."); Console.WriteLine("\tQueueItemId returned from AddToQueue action: {0}", queueItemId); #endregion Call a bound action that requires parameters #region Call a bound custom action that requires parameters Console.WriteLine("\n* Call a bound custom action that requires parameters"); // Add a note to a specified contact. Uses the custom action sample_AddNoteToContact, which // is bound to the contact to annotate, and takes a single param, the note to add. It also // returns the URI to the new annotation. JObject note = JObject.Parse( @"{NoteTitle: 'Sample note', NoteText: 'The text content of the note.'}"); string actionUri = entityUris["Jon Fogg"].ToString() + "/Microsoft.Dynamics.CRM.sample_AddNoteToContact"; JObject contact = svc.Post(actionUri, note); Uri annotationUri = new Uri(svc.BaseAddress + "annotations(" + contact["annotationid"] + ")"); entityUris.Add((string)note["NoteTitle"], annotationUri); Console.WriteLine("\tA note with the title '{0}' was created and " + "associated with the contact 'Jon Fogg'.", note["NoteTitle"]); #endregion Call a bound custom action that requires parameters #region Call an unbound custom action that requires parameters Console.WriteLine("\n* Call an unbound custom action that requires parameters"); // Create a customer of the specified type, using the custom action sample_CreateCustomer, // which takes two parameters: the type of customer ('account' or 'contact') and the name of // the new customer. string customerName = "New account customer (sample)"; JObject customerAttributes = JObject.Parse( @"{CustomerType: 'account', AccountName: '" + customerName + "'}"); JObject response = svc.Post("sample_CreateCustomer", customerAttributes); Console.WriteLine("\tThe account '" + customerName + "' was created."); // Because the CreateCustomer custom action does not return any data about the created instance, // we must query the customer instance to figure out its URI. JToken customer = svc.Get("accounts?$filter=name eq 'New account customer (sample)'&$select=accountid&$top=1"); Uri customerUri = new Uri(svc.BaseAddress + "accounts(" + customer["value"][0]["accountid"] + ")"); entityUris.Add(customerName, customerUri); // Try to call the same custom action with invalid parameters, here the same name is // not valid for a contact. (ContactFirstname and ContactLastName parameters are // required when CustomerType is contact. customerAttributes = JObject.Parse( @"{CustomerType: 'contact', AccountName: '" + customerName + "'}"); try { customerUri = svc.PostCreate("sample_CreateCustomer", customerAttributes); Console.WriteLine("\tCall to the custom CreateCustomer action succeeded, which was not expected."); } catch (ServiceException e) { Console.WriteLine("\tCall to the custom CreateCustomer action did not succeed (as was expected)."); Console.WriteLine($"\t\tError: {e.Message}"); } #endregion Call an unbound custom action that requires parameters DeleteEntityRecords(svc, entityUris); } } catch (Exception e) { Console.BackgroundColor = ConsoleColor.Red; // Highlight exceptions if (e is AggregateException) { foreach (Exception inner in (e as AggregateException).InnerExceptions) { Console.WriteLine("\n" + inner.Message); } } else if (e is ServiceException) { var ex = e as ServiceException; Console.WriteLine("\nMessage send response: status code {0}, {1}", ex.StatusCode, ex.ReasonPhrase); } Console.ReadKey(); // Pause terminal } }
static void Main() { //List of Uris for records created in this sample List <Uri> entityUris = new List <Uri>(); bool deleteCreatedRecords = true; try { using (CDSWebApiService svc = new CDSWebApiService(config)) { Console.WriteLine("--Starting Basic Operations--"); #region Section 1: Basic Create and Update operations Console.WriteLine("--Section 1 started--"); //Create a contact var contact1 = new JObject { { "firstname", "Rafel" }, { "lastname", "Shillo" } }; Uri contact1Uri = svc.PostCreate("contacts", contact1); Console.WriteLine($"Contact '{contact1["firstname"]} " + $"{contact1["lastname"]}' created."); entityUris.Add(contact1Uri); //To delete later Console.WriteLine($"Contact URI: {contact1Uri}"); //Update a contact JObject contact1Add = new JObject { { "annualincome", 80000 }, { "jobtitle", "Junior Developer" } }; svc.Patch(contact1Uri, contact1Add); Console.WriteLine( $"Contact '{contact1["firstname"]} {contact1["lastname"]}' " + $"updated with jobtitle and annual income"); //Retrieve a contact var retrievedcontact1 = svc.Get(contact1Uri.ToString() + "?$select=fullname,annualincome,jobtitle,description"); Console.WriteLine($"Contact '{retrievedcontact1["fullname"]}' retrieved: \n" + $"\tAnnual income: {retrievedcontact1["annualincome"]}\n" + $"\tJob title: {retrievedcontact1["jobtitle"]} \n" + //description is initialized empty. $"\tDescription: {retrievedcontact1["description"]}."); //Modify specific properties and then update entity instance. JObject contact1Update = new JObject { { "jobtitle", "Senior Developer" }, { "annualincome", 95000 }, { "description", "Assignment to-be-determined" } }; svc.Patch(contact1Uri, contact1Update); Console.WriteLine($"Contact '{retrievedcontact1["fullname"]}' updated:\n" + $"\tJob title: {contact1Update["jobtitle"]}\n" + $"\tAnnual income: {contact1Update["annualincome"]}\n" + $"\tDescription: {contact1Update["description"]}\n"); // Change just one property string telephone1 = "555-0105"; svc.Put(contact1Uri.ToString(), "telephone1", telephone1); Console.WriteLine($"Contact '{retrievedcontact1["fullname"]}' " + $"phone number updated."); //Now retrieve just the single property. var telephone1Value = svc.Get($"{contact1Uri}/telephone1"); Console.WriteLine($"Contact's telephone # is: {telephone1Value["value"]}."); #endregion Section 1: Basic Create and Update operations #region Section 2: Create record associated to another /// <summary> /// Demonstrates creation of entity instance and simultaneous association to another, /// existing entity. /// </summary> /// Console.WriteLine("\n--Section 2 started--"); //Create a new account and associate with existing contact in one operation. var account1 = new JObject { { "name", "Contoso Ltd" }, { "telephone1", "555-5555" }, { "*****@*****.**", contact1Uri } }; var account1Uri = svc.PostCreate("accounts", account1); entityUris.Add(account1Uri); //To delete later Console.WriteLine($"Account '{account1["name"]}' created."); Console.WriteLine($"Account URI: {account1Uri}"); //Retrieve account name and primary contact info JObject retrievedAccount1 = svc.Get($"{account1Uri}?$select=name," + $"&$expand=primarycontactid($select=fullname,jobtitle,annualincome)") as JObject; Console.WriteLine($"Account '{retrievedAccount1["name"]}' has primary contact " + $"'{retrievedAccount1["primarycontactid"]["fullname"]}':"); Console.WriteLine($"\tJob title: {retrievedAccount1["primarycontactid"]["jobtitle"]} \n" + $"\tAnnual income: {retrievedAccount1["primarycontactid"]["annualincome"]}"); #endregion Section 2: Create record associated to another #region Section 3: Create related entities /// <summary> /// Demonstrates creation of entity instance and related entities in a single operation. /// </summary> /// Console.WriteLine("\n--Section 3 started--"); //Create the following entries in one operation: an account, its // associated primary contact, and open tasks for that contact. These // entity types have the following relationships: // Accounts // |---[Primary] Contact (N-to-1) // |---Tasks (1-to-N) //Build the Account object inside-out, starting with most nested type(s) JArray tasks = new JArray(); JObject task1 = new JObject { { "subject", "Sign invoice" }, { "description", "Invoice #12321" }, { "scheduledend", DateTimeOffset.Parse("4/19/2019") } }; tasks.Add(task1); JObject task2 = new JObject { { "subject", "Setup new display" }, { "description", "Theme is - Spring is in the air" }, { "scheduledstart", DateTimeOffset.Parse("4/20/2019") } }; tasks.Add(task2); JObject task3 = new JObject { { "subject", "Conduct training" }, { "description", "Train team on making our new blended coffee" }, { "scheduledstart", DateTimeOffset.Parse("6/1/2019") } }; tasks.Add(task3); JObject contact2 = new JObject { { "firstname", "Susie" }, { "lastname", "Curtis" }, { "jobtitle", "Coffee Master" }, { "annualincome", 48000 }, //Add related tasks using corresponding navigation property { "Contact_Tasks", tasks } }; JObject account2 = new JObject { { "name", "Fourth Coffee" }, //Add related contacts using corresponding navigation property { "primarycontactid", contact2 } }; //Create the account and related records Uri account2Uri = svc.PostCreate("accounts", account2); Console.WriteLine($"Account '{account2["name"]} created."); entityUris.Add(account2Uri); //To delete later Console.WriteLine($"Contact URI: {account2Uri}"); //Retrieve account, primary contact info, and assigned tasks for contact. //CDS only supports querying-by-expansion one level deep, so first query // account-primary contact. var retrievedAccount2 = svc.Get($"{account2Uri}?$select=name," + $"&$expand=primarycontactid($select=fullname,jobtitle,annualincome)"); Console.WriteLine($"Account '{retrievedAccount2["name"]}' " + $"has primary contact '{retrievedAccount2["primarycontactid"]["fullname"]}':"); Console.WriteLine($"\tJob title: {retrievedAccount2["primarycontactid"]["jobtitle"]} \n" + $"\tAnnual income: {retrievedAccount2["primarycontactid"]["annualincome"]}"); //Next retrieve same contact and its assigned tasks. //Don't have a saved URI for contact 'Susie Curtis', so create one // from base address and entity ID (and add it to collection for cleanup). Uri contact2Uri = new Uri($"{svc.BaseAddress}contacts({retrievedAccount2["primarycontactid"]["contactid"]})"); entityUris.Add(contact2Uri); //To delete later //Retrieve the contact var retrievedcontact2 = svc.Get($"{contact2Uri}?$select=fullname," + $"&$expand=Contact_Tasks($select=subject,description,scheduledstart,scheduledend)"); Console.WriteLine($"Contact '{retrievedcontact2["fullname"]}' has the following assigned tasks:"); foreach (JToken tk in retrievedcontact2["Contact_Tasks"]) { Console.WriteLine( $"Subject: {tk["subject"]}, \n" + $"\tDescription: {tk["description"]}\n" + $"\tStart: {tk["scheduledstart"].Value<DateTime>():d}\n" + $"\tEnd: {tk["scheduledend"].Value<DateTime>():d}\n"); } #endregion Section 3: Create related entities #region Section 4: Associate and Disassociate entities /// <summary> /// Demonstrates associating and disassociating of existing entity instances. /// </summary> Console.WriteLine("\n--Section 4 started--"); //Add 'Rafel Shillo' to the contact list of 'Fourth Coffee', // a 1-to-N relationship. JObject rel1 = new JObject { { "@odata.id", contact1Uri } }; //relationship object for msg content Uri navUri1 = new Uri($"{account2Uri}/contact_customer_accounts/$ref"); //Create relationship svc.Post(navUri1.ToString(), rel1); Console.WriteLine($"Contact '{retrievedcontact1["fullname"]}' " + $"associated to account '{account2["name"]}'."); //Retrieve and output all contacts for account 'Fourth Coffee'. var retrievedContactList1 = svc.Get($"{account2Uri}/contact_customer_accounts?" + $"$select=fullname,jobtitle"); Console.WriteLine($"Contact list for account '{retrievedAccount2["name"]}':"); foreach (JToken ct in retrievedContactList1["value"]) { Console.WriteLine($"\tName: {ct["fullname"]}, Job title: {ct["jobtitle"]}"); } //Dissociate the contact from the account. For a collection-valued // navigation property, must append URI of referenced entity. var dis1Uri = $"{navUri1}?$id={contact1Uri}"; //Equivalently, could have dissociated from the other end of the // relationship, using the single-valued navigation ref, located in // the contact 'Peter Cambel'. This dissociation URI has a simpler form: // [Org URI]/api/data/v9.1/contacts([contactid#])/parentcustomerid_account/$ref svc.Delete(dis1Uri); //'Rafel Shillo' was removed from the the contact list of 'Fourth Coffee' //Associate an opportunity to a competitor, an N-to-N relationship. //First, create the required entity instances. JObject comp1 = new JObject { { "name", "Adventure Works" }, { "strengths", "Strong promoter of private tours for multi-day outdoor adventures" } }; Uri comp1Uri = svc.PostCreate("competitors", comp1); entityUris.Add(comp1Uri); //To delete later JObject oppor1 = new JObject { ["name"] = "River rafting adventure", ["description"] = "Sales team on a river-rafting offsite and team building" }; Uri oppor1Uri = svc.PostCreate("opportunities", oppor1); entityUris.Add(oppor1Uri); //To delete later //Associate opportunity to competitor via opportunitycompetitors_association. // navigation property. JObject rel2 = new JObject { { "@odata.id", comp1Uri } }; Uri navUri2 = new Uri($"{oppor1Uri}/opportunitycompetitors_association/$ref"); svc.Post(navUri2.ToString(), rel2); Console.WriteLine($"Opportunity '{oppor1["name"]}' associated with competitor '{comp1["name"]}'."); //Retrieve all opportunities for competitor 'Adventure Works'. var retrievedOpporList1 = svc.Get($"{comp1Uri}?$select=name,&$expand=opportunitycompetitors_association($select=name,description)"); Console.WriteLine($"Competitor '{retrievedOpporList1["name"]}' has the following opportunities:"); foreach (JToken op in retrievedOpporList1["opportunitycompetitors_association"]) { Console.WriteLine($"\tName: {op["name"]}, \n" + $"\tDescription: {op["description"]}"); } //Dissociate opportunity from competitor. svc.Delete($"{navUri2}?$id={comp1Uri}"); // 'River rafting adventure' opportunity disassociated with 'Adventure Works' competitor #endregion Section 4: Associate and Disassociate entities #region Section 5: Delete sample entities Console.WriteLine("\n--Section 5 started--"); //Delete all the created sample entities. Note that explicit deletion is not required // for contact tasks because these are automatically cascade-deleted with owner. if (!deleteCreatedRecords) { Console.Write("\nDo you want these entity records deleted? (y/n) [y]: "); String answer = Console.ReadLine(); answer = answer.Trim(); if (!(answer.StartsWith("y") || answer.StartsWith("Y") || answer == string.Empty)) { entityUris.Clear(); } else { Console.WriteLine("\nDeleting created records."); } } else { Console.WriteLine("\nDeleting created records."); } foreach (Uri entityUrl in entityUris) { svc.Delete(entityUrl.ToString()); } #endregion Section 5: Delete sample entities Console.WriteLine("--Basic Operations Completed--"); Console.WriteLine("Press any key to close"); Console.ReadLine(); } } catch (Exception) { throw; } }