Example #1
0
        static async Task <double> ProcessData(CDSWebApiService svc, List <JObject> accountsToImport,
                                               ExecutionDataflowBlockOptions executionDataflowBlockOptions)
        {
            var createAccounts = new TransformBlock <JObject, Uri>(
                async a =>
            {
                return(await svc.PostCreateAsync("accounts", a));
            },
                executionDataflowBlockOptions
                );

            var deleteAccounts = new ActionBlock <Uri>(
                async u =>
            {
                await svc.DeleteAsync(u);
            },
                executionDataflowBlockOptions
                );

            createAccounts.LinkTo(deleteAccounts, new DataflowLinkOptions {
                PropagateCompletion = true
            });

            var start = DateTime.Now;

            accountsToImport.ForEach(a => createAccounts.SendAsync(a));
            createAccounts.Complete();
            await deleteAccounts.Completion;

            //Calculate the duration to complete
            return((DateTime.Now - start).TotalSeconds);
        }
Example #2
0
        static async Task Main()
        {
            #region Optimize Connection

            //Change max connections from .NET to a remote service default: 2
            System.Net.ServicePointManager.DefaultConnectionLimit = 65000;
            //Bump up the min threads reserved for this app to ramp connections faster - minWorkerThreads defaults to 4, minIOCP defaults to 4
            System.Threading.ThreadPool.SetMinThreads(100, 100);
            //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
            System.Net.ServicePointManager.Expect100Continue = false;
            //Can decreas overall transmission overhead but can cause delay in data packet arrival
            System.Net.ServicePointManager.UseNagleAlgorithm = false;

            #endregion Optimize Connection
            var executionDataflowBlockOptions = new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = maxDegreeOfParallelism
            };

            var    count = 0;
            double secondsToComplete;

            //Will be populated with account records to import
            List <JObject> accountsToImport = new List <JObject>();

            Console.WriteLine($"Preparing to create {numberOfRecords} acccount records using Web API.");

            //Add account records to the list to import
            while (count < numberOfRecords)
            {
                var account = new JObject
                {
                    ["name"] = $"Account {count}"
                };
                accountsToImport.Add(account);
                count++;
            }

            using (var svc = new CDSWebApiService(serviceConfig))
            {
                secondsToComplete = await ProcessData(svc, accountsToImport, executionDataflowBlockOptions);
            }

            Console.WriteLine($"Created and deleted {accountsToImport.Count} accounts in  {Math.Round(secondsToComplete)} seconds.");

            Console.WriteLine("Sample completed. Press any key to exit.");
            Console.ReadLine();
        }
        /// <summary>
        /// Create a new entity instance.
        /// </summary>
        /// <param name="svc">The service class used for messaging.</param>
        /// <param name="entitySet">The set name (collection) of the entity.</param>
        /// <param name="entityAttributes">The attributes of the entity.</param>
        /// <returns>The entity's web address (URI).</returns>
        private static Uri CreateEntity(CDSWebApiService svc, string entitySet, JObject entityAttributes)
        {
            Uri    entityUri;
            string name;

            // Singular entity type fron the plural entity set name
            string entityType = entitySet.Remove(entitySet.Length - 1);

            switch (entitySet)
            {
            case "contacts":
                if (entityAttributes.ContainsKey("fullname"))
                {
                    name = (string)entityAttributes["fullname"];
                }
                else
                {
                    name = (string)entityAttributes["firstname"] + " " + entityAttributes["lastname"];
                }
                break;

            case "incidents":
                name = (string)entityAttributes["title"];
                break;

            default:
                name = (string)entityAttributes["name"];
                break;
            }

            try
            {
                Console.Write("\tCreating {0} '{1}'..", entityType, name);
                entityUri = svc.PostCreate(entitySet, entityAttributes);
                Console.WriteLine("completed.");
            }
            catch (ServiceException e)
            {
                Console.WriteLine("failed.");
                throw e;
            }

            return(entityUri);
        }
        /// <summary>
        /// Deletes all entity instances created during program execution.
        /// </summary>
        /// <param name="svc">The service class used for messaging.</param>
        /// <param name="entityUris">A collection of entity instance Uri.</param>
        private static void DeleteEntityRecords(CDSWebApiService svc, Dictionary <string, Uri> entityUris)
        {
            // Delete (or keep) all the created entity records.
            Console.Write("\nDo you want these entity records deleted? (y/n) [y]: ");
            String answer = Console.ReadLine().Trim();

            if (answer.StartsWith("y") || answer.StartsWith("Y") || answer == string.Empty)
            {
                foreach (string entityName in entityUris.Keys)
                {
                    try
                    {
                        Console.Write("Deleting '{0}'..", entityName);
                        svc.Delete(entityUris[entityName].ToString());
                        Console.WriteLine("done");
                    }
                    catch (ServiceException e)
                    { Console.WriteLine("skipping ({0})", e.ReasonPhrase); }
                }
            }
            entityUris.Clear();
        }
Example #5
0
        private static void DeleteRequiredRecords(CDSWebApiService svc, bool deleteCreatedRecords)
        {
            if (!deleteCreatedRecords)
            {
                Console.Write("\nDo you want these sample entity records deleted? (y/n) [y]: ");
                string answer = Console.ReadLine();
                answer = answer.Trim();
                if (!(answer.StartsWith("y") || answer.StartsWith("Y") || answer == string.Empty))
                {
                    return;
                }
            }

            Console.WriteLine("\nDeleting data created for this sample:");

            entityUris.ForEach(x =>
            {
                Console.Write(".");
                svc.Delete(x.ToString());
            });

            Console.WriteLine("\nData created for this sample deleted.");
        }
        /// <summary>
        /// Creates the entity instances used by the main demonstration code.
        /// </summary>
        /// <param name="svc">The service class used for messaging.</param>
        /// <param name="entityUris">A collection of entity instance Uri for tracking and
        /// later deletion (cleanup).</param>
        private static void CreateRequiredRecords(CDSWebApiService svc, Dictionary <string, Uri> entityUris)
        {
            Console.WriteLine("* Creating required records");

            // Create a parent account
            JObject parentAccountAttributes = new JObject()
            {
                { "name", "Fourth Coffee" }
            };

            entityUris.Add("Fourth Coffee",
                           CreateEntity(svc, "accounts", parentAccountAttributes));

            // Create an incident with three associated tasks
            JArray taskAttributess = new JArray(new JObject[]
            {
                JObject.Parse(@"{subject: 'Task 1', actualdurationminutes: 30}"),
                JObject.Parse(@"{subject: 'Task 2', actualdurationminutes: 30}"),
                JObject.Parse(@"{subject: 'Task 3', actualdurationminutes: 30}")
            });

            JObject incidentAttributes = new JObject()
            {
                { "title", "Sample Case" },
                { "*****@*****.**", entityUris["Fourth Coffee"] },
                { "Incident_Tasks", taskAttributess }
            };

            entityUris.Add("Sample Case",
                           CreateEntity(svc, "incidents", incidentAttributes));

            // Close the associated tasks so that they represent completed work
            try
            {
                Console.Write("\tSetting each incident task to completed..");
                JToken incidentTaskRefs = svc.Get(entityUris["Sample Case"] + "/Incident_Tasks/$ref");

                // Property value set used to mark tasks as completed.
                JObject completeCode = JObject.Parse(@"{statecode: 1, statuscode: 5}");

                foreach (JToken taskId in incidentTaskRefs["value"])
                {
                    var taskUri = new Uri(taskId["@odata.id"].ToString());
                    svc.Patch(taskUri, completeCode);
                }
                Console.WriteLine("done.");
            }
            catch (ServiceException e)
            {
                Console.WriteLine("failed.");
                throw e;
            }

            //Create another account and associated opportunity (required for CloseOpportunityAsWon)
            JObject wineryAccountAttributes = new JObject()
            {
                { "name", "Coho Winery" },
                { "opportunity_customer_accounts", JArray.Parse(@"[{ name: 'Opportunity to win' }]") }
            };

            entityUris.Add("Coho Winery",
                           CreateEntity(svc, "accounts", wineryAccountAttributes));

            //Retrieve the URI of the associated opportunity
            try
            {
                Console.Write("\tRetrieving the URI of the opportunity 'Opportunity to win'..");
                JToken custOpporRefs = svc.Get(entityUris["Coho Winery"] + "/opportunity_customer_accounts/$ref");

                entityUris.Add("Opportunity to win",
                               new Uri(custOpporRefs["value"][0]["@odata.id"].ToString()));
                Console.WriteLine("completed.");
            }
            catch (ServiceException e)
            {
                Console.WriteLine("failed.");
                throw e;
            }

            //Create a contact to use with custom action sample_AddNoteToContact
            JObject contactAttributes = JObject.Parse(@"{firstname: 'Jon', lastname: 'Fogg'}");

            entityUris.Add("Jon Fogg",
                           CreateEntity(svc, "contacts", contactAttributes));

            Console.WriteLine("* Finished creating required records");
        }
        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
            }
        }
Example #8
0
        private static void CreateRequiredRecords(CDSWebApiService svc)
        {
            var account1 = new JObject {
                { "name", "Contoso, Ltd. (sample)" },
                { "Account_Tasks", new JArray {
                      new JObject {
                          { "subject", "Task 1 for Contoso, Ltd." },
                          { "description", "Task 1 for Contoso, Ltd. description" },
                          { "actualdurationminutes", 10 }
                      },
                      new JObject {
                          { "subject", "Task 2 for Contoso, Ltd." },
                          { "description", "Task 2 for Contoso, Ltd. description" },
                          { "actualdurationminutes", 10 }
                      },
                      new JObject {
                          { "subject", "Task 3 for Contoso, Ltd." },
                          { "description", "Task 3 for Contoso, Ltd. description" },
                          { "actualdurationminutes", 10 }
                      },
                  } },
                { "primarycontactid", new JObject {
                      { "firstname", "Yvonne" },
                      { "lastname", "McKay (sample)" },
                      { "jobtitle", "Coffee Master" },
                      { "annualincome", 45000 },
                      { "Contact_Tasks", new JArray {
                            new JObject {
                                { "subject", "Task 1 for Yvonne McKay" },
                                { "description", "Task 1 for Yvonne McKay description" },
                                { "actualdurationminutes", 5 }
                            },
                            new JObject {
                                { "subject", "Task 2 for Yvonne McKay" },
                                { "description", "Task 2 for Yvonne McKay description" },
                                { "actualdurationminutes", 5 }
                            },
                            new JObject {
                                { "subject", "Task 3 for Yvonne McKay" },
                                { "description", "Task 3 for Yvonne McKay description" },
                                { "actualdurationminutes", 5 }
                            },
                        } }
                  } },
                { "contact_customer_accounts", new JArray {
                      new JObject {
                          { "firstname", "Susanna" },
                          { "lastname", "Stubberod (sample)" },
                          { "jobtitle", "Senior Purchaser" },
                          { "annualincome", 52000 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Susanna Stubberod" },
                                    { "description", "Task 1 for Susanna Stubberod description" },
                                    { "actualdurationminutes", 3 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Susanna Stubberod" },
                                    { "description", "Task 2 for Susanna Stubberod description" },
                                    { "actualdurationminutes", 3 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Susanna Stubberod" },
                                    { "description", "Task 3 for Susanna Stubberod description" },
                                    { "actualdurationminutes", 3 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Nancy" },
                          { "lastname", "Anderson (sample)" },
                          { "jobtitle", "Activities Manager" },
                          { "annualincome", 55500 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Nancy Anderson" },
                                    { "description", "Task 1 for Nancy Anderson description" },
                                    { "actualdurationminutes", 4 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Nancy Anderson" },
                                    { "description", "Task 2 for Nancy Anderson description" },
                                    { "actualdurationminutes", 4 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Nancy Anderson" },
                                    { "description", "Task 3 for Nancy Anderson description" },
                                    { "actualdurationminutes", 4 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Maria" },
                          { "lastname", "Cambell (sample)" },
                          { "jobtitle", "Accounts Manager" },
                          { "annualincome", 31000 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Maria Cambell" },
                                    { "description", "Task 1 for Maria Cambell description" },
                                    { "actualdurationminutes", 5 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Maria Cambell" },
                                    { "description", "Task 2 for Maria Cambell description" },
                                    { "actualdurationminutes", 5 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Maria Cambell" },
                                    { "description", "Task 3 for Maria Cambell description" },
                                    { "actualdurationminutes", 5 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Scott" },
                          { "lastname", "Konersmann (sample)" },
                          { "jobtitle", "Accounts Manager" },
                          { "annualincome", 38000 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Scott Konersmann" },
                                    { "description", "Task 1 for Scott Konersmann description" },
                                    { "actualdurationminutes", 6 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Scott Konersmann" },
                                    { "description", "Task 2 for Scott Konersmann description" },
                                    { "actualdurationminutes", 6 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Scott Konersmann" },
                                    { "description", "Task 3 for Scott Konersmann description" },
                                    { "actualdurationminutes", 6 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Robert" },
                          { "lastname", "Lyon (sample)" },
                          { "jobtitle", "Senior Technician" },
                          { "annualincome", 78000 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Robert Lyon" },
                                    { "description", "Task 1 for Robert Lyon description" },
                                    { "actualdurationminutes", 7 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Robert Lyon" },
                                    { "description", "Task 2 for Robert Lyon description" },
                                    { "actualdurationminutes", 7 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Robert Lyon" },
                                    { "description", "Task 3 for Robert Lyon description" },
                                    { "actualdurationminutes", 7 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Paul" },
                          { "lastname", "Cannon (sample)" },
                          { "jobtitle", "Ski Instructor" },
                          { "annualincome", 68500 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Paul Cannon" },
                                    { "description", "Task 1 for Paul Cannon description" },
                                    { "actualdurationminutes", 8 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Paul Cannon" },
                                    { "description", "Task 2 for Paul Cannon description" },
                                    { "actualdurationminutes", 8 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Paul Cannon" },
                                    { "description", "Task 3 for Paul Cannon description" },
                                    { "actualdurationminutes", 8 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Rene" },
                          { "lastname", "Valdes (sample)" },
                          { "jobtitle", "Data Analyst III" },
                          { "annualincome", 86000 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Rene Valdes" },
                                    { "description", "Task 1 for Rene Valdes description" },
                                    { "actualdurationminutes", 9 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Rene Valdes" },
                                    { "description", "Task 2 for Rene Valdes description" },
                                    { "actualdurationminutes", 9 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Rene Valdes" },
                                    { "description", "Task 3 for Rene Valdes description" },
                                    { "actualdurationminutes", 9 }
                                },
                            } }
                      },
                      new JObject {
                          { "firstname", "Jim" },
                          { "lastname", "Glynn (sample)" },
                          { "jobtitle", "Senior International Sales Manager" },
                          { "annualincome", 81400 },
                          { "Contact_Tasks", new JArray {
                                new JObject {
                                    { "subject", "Task 1 for Jim Glynn" },
                                    { "description", "Task 1 for Jim Glynn description" },
                                    { "actualdurationminutes", 10 }
                                },
                                new JObject {
                                    { "subject", "Task 2 for Jim Glynn" },
                                    { "description", "Task 2 for Jim Glynn description" },
                                    { "actualdurationminutes", 10 }
                                },
                                new JObject {
                                    { "subject", "Task 3 for Jim Glynn" },
                                    { "description", "Task 3 for Jim Glynn description" },
                                    { "actualdurationminutes", 10 }
                                },
                            } }
                      }
                  } }
            };

            account1Uri = svc.PostCreate("accounts", account1);
            entityUris.Add(account1Uri);
            contact1Uri = new Uri((svc.Get($"{account1Uri}/primarycontactid/$ref"))["@odata.id"].ToString());
            entityUris.Add(contact1Uri);
            var contact_customer_accounts = svc.Get($"{account1Uri}/contact_customer_accounts/$ref");

            foreach (JObject contactRef in contact_customer_accounts["value"])
            {
                //Add to the top of the list so these are deleted first
                entityUris.Insert(0, new Uri(contactRef["@odata.id"].ToString()));
            }
            //The related tasks will be deleted automatically when the owning record is deleted.
            Console.WriteLine("Account 'Contoso, Ltd. (sample)' created with 1 primary " +
                              "contact and 8 associated contacts.");
        }
Example #9
0
        private static void Main()
        {
            try
            {
                using (CDSWebApiService svc = new CDSWebApiService(config))
                {
                    Console.WriteLine("\n--Starting Query Data --");
                    //Create the records that this sample will query.
                    CreateRequiredRecords(svc);

                    //Get the id and name of the account created to use as a filter.
                    var    contoso     = svc.Get($"{account1Uri}?$select=accountid,name");
                    var    contosoId   = Guid.Parse(contoso["accountid"].ToString());
                    string contosoName = (string)contoso["name"];

                    #region Selecting specific properties

                    // Basic query: Query using $select against a contact entity to get the properties you want.
                    // For performance best practice, always use $select, otherwise all properties are returned
                    Console.WriteLine("-- Basic Query --");

                    //Header required to include formatted values
                    var formattedValueHeaders = new Dictionary <string, List <string> > {
                        { "Prefer", new List <string>
                          {
                              "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\""
                          } }
                    };

                    var contact1 = svc.Get(
                        $"{contact1Uri}?$select=fullname,jobtitle,annualincome",
                        formattedValueHeaders);

                    Console.WriteLine($"Contact basic info:\n" +
                                      $"\tFullname: {contact1["fullname"]}\n" +
                                      $"\tJobtitle: {contact1["jobtitle"]}\n" +
                                      $"\tAnnualincome (unformatted): {contact1["annualincome"]} \n" +
                                      $"\tAnnualincome (formatted): {contact1["*****@*****.**"]} \n");

                    #endregion Selecting specific properties

                    #region Using query functions

                    // Filter criteria:
                    // Applying filters to get targeted data.
                    // 1) Using standard query functions (e.g.: contains, endswith, startswith)
                    // 2) Using CDS query functions (e.g.: LastXhours, Last7Days, Today, Between, In, ...)
                    // 3) Using filter operators and logical operators (e.g.: eq, ne, gt, and, or, etc…)
                    // 4) Set precedence using parenthesis (e.g.: ((criteria1) and (criteria2)) or (criteria3)
                    // For more info, see:
                    //https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/webapi/query-data-web-api#filter-results

                    Console.WriteLine("-- Filter Criteria --");
                    //Filter 1: Using standard query functions to filter results.  In this operation, we
                    //will query for all contacts with fullname containing the string "(sample)".
                    JToken containsSampleinFullNameCollection = svc.Get("contacts?" +
                                                                        "$select=fullname,jobtitle,annualincome&" +
                                                                        "$filter=contains(fullname,'(sample)') and " +
                                                                        $"_parentcustomerid_value eq {contosoId}",
                                                                        formattedValueHeaders);

                    WriteContactResultsTable(
                        "Contacts filtered by fullname containing '(sample)':",
                        containsSampleinFullNameCollection["value"]);

                    //Filter 2: Using CDS query functions to filter results. In this operation, we will query
                    //for all contacts that were created in the last hour. For complete list of CDS query
                    //functions, see: https://docs.microsoft.com/dynamics365/customer-engagement/web-api/queryfunctions

                    JToken createdInLastHourCollection = svc.Get("contacts?" +
                                                                 "$select=fullname,jobtitle,annualincome&" +
                                                                 "$filter=Microsoft.Dynamics.CRM.LastXHours(PropertyName='createdon',PropertyValue='1') and " +
                                                                 $"_parentcustomerid_value eq {contosoId}",
                                                                 formattedValueHeaders);

                    WriteContactResultsTable(
                        "Contacts that were created within the last 1hr:",
                        createdInLastHourCollection["value"]);

                    //Filter 3: Using operators. Building on the previous operation, we further limit
                    //the results by the contact's income. For more info on standard filter operators,
                    //https://docs.microsoft.com/powerapps/developer/common-data-service/webapi/query-data-web-api#filter-results

                    JToken highIncomeContacts = svc.Get("contacts?" +
                                                        "$select=fullname,jobtitle,annualincome&" +
                                                        "$filter=contains(fullname,'(sample)') and " +
                                                        "annualincome gt 55000  and " +
                                                        $"_parentcustomerid_value eq {contosoId}",
                                                        formattedValueHeaders);

                    WriteContactResultsTable(
                        "Contacts with '(sample)' in name and income above $55,000:",
                        highIncomeContacts["value"]);

                    //Filter 4: Set precedence using parentheses. Continue building on the previous
                    //operation, we further limit results by job title. Parentheses and the order of
                    //filter statements can impact results returned.

                    JToken seniorOrSpecialistsCollection = svc.Get("contacts?" +
                                                                   "$select=fullname,jobtitle,annualincome&" +
                                                                   "$filter=contains(fullname,'(sample)') and " +
                                                                   "(contains(jobtitle, 'senior') or " +
                                                                   "contains(jobtitle,'manager')) and " +
                                                                   "annualincome gt 55000 and " +
                                                                   $"_parentcustomerid_value eq {contosoId}",
                                                                   formattedValueHeaders);

                    WriteContactResultsTable(
                        "Contacts with '(sample)' in name senior jobtitle or high income:",
                        seniorOrSpecialistsCollection["value"]);

                    #endregion Using query functions

                    #region Ordering and aliases

                    //Results can be ordered in descending or ascending order.
                    Console.WriteLine("\n-- Order Results --");

                    JToken orderedResults = svc.Get("contacts?" +
                                                    "$select=fullname,jobtitle,annualincome&" +
                                                    "$filter=contains(fullname,'(sample)')and " +
                                                    $"_parentcustomerid_value eq {contosoId}&" +
                                                    "$orderby=jobtitle asc, annualincome desc",
                                                    formattedValueHeaders);

                    WriteContactResultsTable(
                        "Contacts ordered by jobtitle (Ascending) and annualincome (descending)",
                        orderedResults["value"]);

                    //Parameterized aliases can be used as parameters in a query. These parameters can be used
                    //in $filter and $orderby options. Using the previous operation as basis, parameterizing the
                    //query will give us the same results. For more info, see:
                    //https://docs.microsoft.com/powerapps/developer/common-data-service/webapi/use-web-api-functions#passing-parameters-to-a-function

                    Console.WriteLine("\n-- Parameterized Aliases --");

                    JToken orderedResultsWithParams = svc.Get("contacts?" +
                                                              "$select=fullname,jobtitle,annualincome&" +
                                                              "$filter=contains(@p1,'(sample)') and " +
                                                              "@p2 eq @p3&" +
                                                              "$orderby=@p4 asc, @p5 desc&" +
                                                              "@p1=fullname&" +
                                                              "@p2=_parentcustomerid_value&" +
                                                              $"@p3={contosoId}&" +
                                                              "@p4=jobtitle&" +
                                                              "@p5=annualincome",
                                                              formattedValueHeaders);

                    WriteContactResultsTable(
                        "Contacts ordered by jobtitle (Ascending) and annualincome (descending)",
                        orderedResultsWithParams["value"]);

                    #endregion Ordering and aliases

                    #region Limit results

                    //To limit records returned, use the $top query option.  Specifying a limit number for $top
                    //returns at most that number of results per request. Extra results are ignored.
                    //For more information, see:
                    // https://docs.microsoft.com/powerapps/developer/common-data-service/webapi/query-data-web-api#use-top-query-option
                    Console.WriteLine("\n-- Top Results --");

                    JToken topFive = svc.Get("contacts?" +
                                             "$select=fullname,jobtitle,annualincome&" +
                                             "$filter=contains(fullname,'(sample)') and " +
                                             $"_parentcustomerid_value eq {contosoId}&" +
                                             "$top=5",
                                             formattedValueHeaders);

                    WriteContactResultsTable("Contacts top 5 results:", topFive["value"]);

                    //Result count - count the number of results matching the filter criteria.
                    //Tip: Use count together with the "odata.maxpagesize" to calculate the number of pages in
                    //the query.  Note: CDS has a max record limit of 5000 records per response.
                    Console.WriteLine("\n-- Result Count --");
                    //1) Get a count of a collection without the data.
                    JToken count = svc.Get($"contacts/$count");
                    Console.WriteLine($"\nThe contacts collection has {count} contacts.");
                    //  2) Get a count along with the data.

                    JToken countWithData = svc.Get("contacts?" +
                                                   "$select=fullname,jobtitle,annualincome&" +
                                                   "$filter=(contains(jobtitle,'senior') or contains(jobtitle, 'manager')) and " +
                                                   $"_parentcustomerid_value eq {contosoId}" +
                                                   "&$count=true",
                                                   formattedValueHeaders);

                    WriteContactResultsTable($"{countWithData["@odata.count"]} " +
                                             $"Contacts with 'senior' or 'manager' in job title:",
                                             countWithData["value"]);

                    #endregion Limit results

                    #region Expanding results

                    //The expand option retrieves related information.
                    //To retrieve information on associated entities in the same request, use the $expand
                    //query option on navigation properties.
                    //  1) Expand using single-valued navigation properties (e.g.: via the 'primarycontactid')
                    //  2) Expand using partner property (e.g.: from contact to account via the 'account_primary_contact')
                    //  3) Expand using collection-valued navigation properties (e.g.: via the 'contact_customer_accounts')
                    //  4) Expand using multiple navigation property types in a single request.
                    //  5) Multi-level expands

                    // Tip: For performance best practice, always use $select statement in an expand option.
                    Console.WriteLine("\n-- Expanding Results --");

                    //1) Expand using the 'primarycontactid' single-valued navigation property of account1.

                    JToken account1 = svc.Get($"{account1Uri}?" +
                                              "$select=name&" +
                                              "$expand=primarycontactid($select=fullname,jobtitle,annualincome)");

                    Console.WriteLine($"\nAccount {account1["name"]} has the following primary contact person:\n" +
                                      $"\tFullname: {account1["primarycontactid"]["fullname"]} \n" +
                                      $"\tJobtitle: {account1["primarycontactid"]["jobtitle"]} \n" +
                                      $"\tAnnualincome: { account1["primarycontactid"]["annualincome"]}");

                    //2) Expand using the 'account_primary_contact' partner property.

                    JToken contact2 = svc.Get($"{contact1Uri}?$select=fullname,jobtitle,annualincome&" +
                                              "$expand=account_primary_contact($select=name)");

                    Console.WriteLine($"\nContact '{contact2["fullname"]}' is the primary contact for the following accounts:");
                    foreach (JObject account in contact2["account_primary_contact"])
                    {
                        Console.WriteLine($"\t{account["name"]}");
                    }

                    //3) Expand using the collection-valued 'contact_customer_accounts' navigation property.

                    JToken account2 = svc.Get($"{account1Uri}?" +
                                              "$select=name&" +
                                              "$expand=contact_customer_accounts($select=fullname,jobtitle,annualincome)",
                                              formattedValueHeaders);

                    WriteContactResultsTable(
                        $"Account '{account2["name"]}' has the following contact customers:",
                        account2["contact_customer_accounts"]);

                    //4) Expand using multiple navigation property types in a single request, specifically:
                    //   primarycontactid, contact_customer_accounts, and Account_Tasks.

                    Console.WriteLine("\n-- Expanding multiple property types in one request -- ");

                    JToken account3 = svc.Get($"{account1Uri}?$select=name&" +
                                              "$expand=primarycontactid($select=fullname,jobtitle,annualincome)," +
                                              "contact_customer_accounts($select=fullname,jobtitle,annualincome)," +
                                              "Account_Tasks($select=subject,description)",
                                              formattedValueHeaders);

                    Console.WriteLine($"\nAccount {account3["name"]} has the following primary contact person:\n" +
                                      $"\tFullname: {account3["primarycontactid"]["fullname"]} \n" +
                                      $"\tJobtitle: {account3["primarycontactid"]["jobtitle"]} \n" +
                                      $"\tAnnualincome: {account3["primarycontactid"]["annualincome"]}");

                    WriteContactResultsTable(
                        $"Account '{account3["name"]}' has the following contact customers:",
                        account3["contact_customer_accounts"]);

                    Console.WriteLine($"\nAccount '{account3["name"] }' has the following tasks:");

                    foreach (JObject task in account3["Account_Tasks"])
                    {
                        Console.WriteLine($"\t{task["subject"]}");
                    }

                    // 5) Multi-level expands

                    //The following query applies nested expands to single-valued navigation properties
                    // starting with Task entities related to contacts created for this sample.
                    JToken contosoTasks = svc.Get($"tasks?" +
                                                  $"$select=subject&" +
                                                  $"$filter=regardingobjectid_contact_task/_accountid_value eq {contosoId}" +
                                                  $"&$expand=regardingobjectid_contact_task($select=fullname;" +
                                                  $"$expand=parentcustomerid_account($select=name;" +
                                                  $"$expand=createdby($select=fullname)))",
                                                  formattedValueHeaders);

                    Console.WriteLine("\nExpanded values from Task:");

                    DisplayExpandedValuesFromTask(contosoTasks["value"]);

                    #endregion Expanding results

                    #region Aggregate results

                    //Get aggregated salary information about Contacts working for Contoso

                    Console.WriteLine("\nAggregated Annual Income information for Contoso contacts:");

                    JToken contactData = svc.Get($"{account1Uri}/contact_customer_accounts?" +
                                                 $"$apply=aggregate(annualincome with average as average, " +
                                                 $"annualincome with sum as total, " +
                                                 $"annualincome with min as minimum, " +
                                                 $"annualincome with max as maximum)", formattedValueHeaders);

                    Console.WriteLine($"\tAverage income: {contactData["value"][0]["*****@*****.**"]}");
                    Console.WriteLine($"\tTotal income: {contactData["value"][0]["*****@*****.**"]}");
                    Console.WriteLine($"\tMinium income: {contactData["value"][0]["*****@*****.**"]}");
                    Console.WriteLine($"\tMaximum income: {contactData["value"][0]["*****@*****.**"]}");



                    #endregion Aggregate results

                    #region FetchXML queries

                    //Use FetchXML to query for all contacts whose fullname contains '(sample)'.
                    //Note: XML string must be URI encoded. For more information, see:
                    //https://docs.microsoft.com/powerapps/developer/common-data-service/webapi/retrieve-and-execute-predefined-queries#use-custom-fetchxml
                    Console.WriteLine("\n-- FetchXML -- ");
                    string fetchXmlQuery =
                        "<fetch mapping='logical' output-format='xml-platform' version='1.0' distinct='false'>" +
                        "<entity name ='contact'>" +
                        "<attribute name ='fullname' />" +
                        "<attribute name ='jobtitle' />" +
                        "<attribute name ='annualincome' />" +
                        "<order descending ='true' attribute='fullname' />" +
                        "<filter type ='and'>" +
                        "<condition value ='%(sample)%' attribute='fullname' operator='like' />" +
                        $"<condition value ='{contosoId}' attribute='parentcustomerid' operator='eq' />" +
                        "</filter>" +
                        "</entity>" +
                        "</fetch>";
                    JToken contacts = svc.Get(
                        $"contacts?fetchXml={WebUtility.UrlEncode(fetchXmlQuery)}",
                        formattedValueHeaders);

                    WriteContactResultsTable($"Contacts Fetched by fullname containing '(sample)':", contacts["value"]);

                    #endregion FetchXML queries

                    #region Using predefined queries

                    //Use predefined queries of the following two types:
                    //  1) Saved query (system view)
                    //  2) User query (saved view)
                    //For more info, see:
                    //https://docs.microsoft.com/powerapps/developer/common-data-service/webapi/retrieve-and-execute-predefined-queries#predefined-queries

                    //1) Saved Query - retrieve "Active Accounts", run it, then display the results.
                    Console.WriteLine("\n-- Saved Query -- ");

                    JToken savedqueryid = svc.Get("savedqueries?" +
                                                  "$select=name,savedqueryid&" +
                                                  "$filter=name eq 'Active Accounts'")["value"][0]["savedqueryid"];

                    var activeAccounts = svc.Get(
                        $"accounts?savedQuery={savedqueryid}",
                        formattedValueHeaders)["value"] as JArray;

                    DisplayFormattedEntities("\nActive Accounts", activeAccounts, new string[] { "name" });

                    //2) Create a user query, then retrieve and execute it to display its results.
                    //For more info, see:
                    //https://docs.microsoft.com/powerapps/developer/common-data-service/saved-queries
                    Console.WriteLine("\n-- User Query -- ");
                    var userQuery = new JObject
                    {
                        ["name"]             = "My User Query",
                        ["description"]      = "User query to display contact info.",
                        ["querytype"]        = 0,
                        ["returnedtypecode"] = "contact",
                        ["fetchxml"]         = @"<fetch mapping='logical' output-format='xml-platform' version='1.0' distinct='false'>
                    <entity name ='contact'>
                        <attribute name ='fullname' />
                        <attribute name ='contactid' />
                        <attribute name ='jobtitle' />
                        <attribute name ='annualincome' />
                        <order descending ='false' attribute='fullname' />
                        <filter type ='and'>
                            <condition value ='%(sample)%' attribute='fullname' operator='like' />
                            <condition value ='%Manager%' attribute='jobtitle' operator='like' />
                            <condition value ='55000' attribute='annualincome' operator='gt' />
                        </filter>
                    </entity>
                 </fetch>"
                    };

                    //Create the saved query
                    Uri myUserQueryUri = svc.PostCreate("userqueries", userQuery);
                    entityUris.Add(myUserQueryUri); //To delete later
                                                    //Retrieve the userqueryid
                    JToken myUserQueryId = svc.Get($"{myUserQueryUri}/userqueryid")["value"];
                    //Use the query to return results:
                    JToken myUserQueryResults = svc.Get($"contacts?userQuery={myUserQueryId}", formattedValueHeaders)["value"];

                    WriteContactResultsTable($"Contacts Fetched by My User Query:", myUserQueryResults);

                    #endregion Using predefined queries

                    DeleteRequiredRecords(svc, deleteCreatedRecords);

                    Console.WriteLine("\n--Query Data Completed--");
                    Console.WriteLine("Press any key to close");
                    Console.ReadLine();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
Example #10
0
        static void Main()
        {
            #region Optimize Connection

            //Change max connections from .NET to a remote service default: 2
            System.Net.ServicePointManager.DefaultConnectionLimit = 65000;
            //Bump up the min threads reserved for this app to ramp connections faster - minWorkerThreads defaults to 4, minIOCP defaults to 4
            System.Threading.ThreadPool.SetMinThreads(100, 100);
            //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
            System.Net.ServicePointManager.Expect100Continue = false;
            //Can decreas overall transmission overhead but can cause delay in data packet arrival
            System.Net.ServicePointManager.UseNagleAlgorithm = false;

            #endregion Optimize Connection

            var parallelOptions = new ParallelOptions()
            {
                MaxDegreeOfParallelism = maxDegreeOfParallelism
            };

            var count = 0;

            //Will be populated with account records to import
            List <JObject> accountsToImport = new List <JObject>();
            //ConcurrentBag is a thread-safe, unordered collection of objects.
            ConcurrentBag <Uri> accountsToDelete = new ConcurrentBag <Uri>();
            Console.WriteLine($"Preparing to create {numberOfRecords} acccount records using Web API.");

            //Add account records to the list to import
            while (count < numberOfRecords)
            {
                var account = new JObject
                {
                    ["name"] = $"Account {count}"
                };
                accountsToImport.Add(account);
                count++;
            }


            try
            {
                using (CDSWebApiService svc = new CDSWebApiService(serviceConfig))
                {
                    Console.WriteLine($"Creating {accountsToImport.Count} accounts");
                    var startCreate = DateTime.Now;

                    //Create the accounts in parallel
                    Parallel.ForEach(accountsToImport, parallelOptions, (account) =>

                    {
                        //Add the Uri returned to the ConcurrentBag to delete later
                        accountsToDelete.Add(svc.PostCreate("accounts", account));
                    });

                    //Calculate the duration to complete
                    var secondsToCreate = (DateTime.Now - startCreate).TotalSeconds;

                    Console.WriteLine($"Created {accountsToImport.Count} accounts in  {Math.Round(secondsToCreate)} seconds.");


                    Console.WriteLine($"Deleting {accountsToDelete.Count} accounts");
                    var startDelete = DateTime.Now;

                    //Delete the accounts in parallel
                    Parallel.ForEach(accountsToDelete, parallelOptions, (uri) =>
                    {
                        svc.Delete(uri.ToString());
                    });

                    //Calculate the duration to complete
                    var secondsToDelete = (DateTime.Now - startDelete).TotalSeconds;

                    Console.WriteLine($"Deleted {accountsToDelete.Count} accounts in {Math.Round(secondsToDelete)} seconds.");
                    Console.WriteLine("Sample completed. Press any key to exit.");
                    Console.ReadLine();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
Example #11
0
        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;
            }
        }
Example #12
0
        static void Main()
        {
            // Save the URIs for entity records created in this sample. so they
            // can be deleted later.
            List <Uri> entityUris = new List <Uri>();

            try
            {
                // Use the wrapper class that handles message processing, error handling, and more.
                using (CDSWebApiService svc = new CDSWebApiService(config))
                {
                    Console.WriteLine("--Starting conditional operations demonstration--\n");

                    #region Create required records
                    // Create an account record
                    var account1 = new JObject {
                        { "name", "Contoso Ltd" },
                        { "telephone1", "555-0000" }, //Phone number value increments with each update attempt
                        { "revenue", 5000000 },
                        { "description", "Parent company of Contoso Pharmaceuticals, etc." }
                    };

                    Uri account1Uri = svc.PostCreate("accounts", account1);
                    entityUris.Add(account1Uri); // Track any created records

                    // Retrieve the account record that was just created.
                    string queryOptions      = "?$select=name,revenue,telephone1,description";
                    var    retrievedaccount1 = svc.Get(account1Uri.ToString() + queryOptions);

                    // Store the ETag value from the retrieved record
                    string initialAcctETagVal = retrievedaccount1["@odata.etag"].ToString();

                    Console.WriteLine("Created and retrieved the initial account, shown below:");
                    Console.WriteLine(retrievedaccount1.ToString(Formatting.Indented));
                    #endregion Create required records

                    #region Conditional GET
                    Console.WriteLine("\n** Conditional GET demonstration **");

                    // Attempt to retrieve the account record using a conditional GET defined by a message header with
                    // the current ETag value.
                    try
                    {
                        retrievedaccount1 = svc.Get(
                            path: account1Uri.ToString() + queryOptions,
                            headers: new Dictionary <string, List <string> > {
                            { "If-None-Match", new List <string> {
                                  initialAcctETagVal
                              } }
                        }
                            );

                        // Not expected; the returned response contains content.
                        Console.WriteLine("Instance retrieved using ETag: {0}", initialAcctETagVal);
                        Console.WriteLine(retrievedaccount1.ToString(Formatting.Indented));
                    }
                    catch (AggregateException ae) // Message was not successful
                    {
                        ae.Handle((x) =>
                        {
                            if (x is ServiceException) // This we know how to handle.
                            {
                                var e = x as ServiceException;
                                if (e.StatusCode == (int)HttpStatusCode.NotModified) // Expected result
                                {
                                    Console.WriteLine("Account record retrieved using ETag: {0}", initialAcctETagVal);
                                    Console.WriteLine("Expected outcome: Entity was not modified so nothing was returned.");
                                    return(true);
                                }
                            }
                            return(false); // Let anything else stop the application.
                        });
                    }

                    // Modify the account instance by updating the telephone1 attribute
                    svc.Put(account1Uri, "telephone1", "555-0001");
                    Console.WriteLine("\n\bAccount telephone number updated to '555-0001'.\n");

                    // Re-attempt to retrieve using conditional GET defined by a message header with
                    // the current ETag value.
                    try
                    {
                        retrievedaccount1 = svc.Get(
                            path: account1Uri.ToString() + queryOptions,
                            headers: new Dictionary <string, List <string> > {
                            { "If-None-Match", new List <string> {
                                  initialAcctETagVal
                              } }
                        }
                            );

                        // Expected result
                        Console.WriteLine("Modified account record retrieved using ETag: {0}", initialAcctETagVal);
                        Console.WriteLine("Notice the update ETag value and telephone number");
                    }
                    catch (ServiceException e)
                    {
                        if (e.StatusCode == (int)HttpStatusCode.NotModified) // Not expected
                        {
                            Console.WriteLine("Unexpected outcome: Entity was modified so something should be returned.");
                        }
                        else
                        {
                            throw e;
                        }
                    }

                    // Save the updated ETag value
                    var updatedAcctETagVal = retrievedaccount1["@odata.etag"].ToString();

                    // Display ty updated record
                    Console.WriteLine(retrievedaccount1.ToString(Formatting.Indented));
                    #endregion Conditional GET

                    #region Optimistic concurrency on delete and update
                    Console.WriteLine("\n** Optimistic concurrency demonstration **");

                    // Attempt to delete original account (if matches original ETag value).
                    // If you replace "initialAcctETagVal" with "updatedAcctETagVal", the delete will
                    // succeed. However, we want the delete to fail for now to demonstrate use of the ETag.
                    Console.WriteLine("Attempting to delete the account using the original ETag value");

                    try
                    {
                        svc.Delete(
                            uri: account1Uri,
                            headers: new Dictionary <string, List <string> > {
                            { "If-Match", new List <string> {
                                  initialAcctETagVal
                              } }
                        }
                            );

                        // Not expected; this code should not execute.
                        Console.WriteLine("Account deleted");
                    }
                    catch (ServiceException e)
                    {
                        if (e.StatusCode == (int)HttpStatusCode.PreconditionFailed) // Expected result
                        {
                            Console.WriteLine("Expected Error: The version of the account record no" +
                                              " longer matches the original ETag.");
                            Console.WriteLine("\tAccount not deleted using ETag '{0}', status code: '{1}'.",
                                              initialAcctETagVal, e.StatusCode);
                        }
                        else
                        {
                            throw e;
                        }
                    }

                    Console.WriteLine("Attempting to update the account using the original ETag value");
                    JObject accountUpdate = new JObject()
                    {
                        { "telephone1", "555-0002" },
                        { "revenue", 6000000 }
                    };

                    try
                    {
                        svc.Patch(
                            uri: account1Uri,
                            body: accountUpdate,
                            headers: new Dictionary <string, List <string> > {
                            { "If-Match", new List <string> {
                                  initialAcctETagVal
                              } }
                        }
                            );

                        // Not expected; this code should not execute.
                        Console.WriteLine("Account updated using original ETag {0}", initialAcctETagVal);
                    }
                    catch (ServiceException e)
                    {
                        if (e.StatusCode == (int)HttpStatusCode.PreconditionFailed) // Expected error
                        {
                            Console.WriteLine("Expected Error: The version of the existing record doesn't "
                                              + "match the ETag property provided.");
                            Console.WriteLine("\tAccount not updated using ETag '{0}', status code: '{1}'.",
                                              initialAcctETagVal, (int)e.StatusCode);
                        }
                        else
                        {
                            throw e;
                        }
                    }

                    // Reattempt update if matches current ETag value.
                    accountUpdate["telephone1"] = "555-0003";
                    Console.WriteLine("Attempting to update the account using the current ETag value");
                    try
                    {
                        svc.Patch(
                            uri: account1Uri,
                            body: accountUpdate,
                            headers: new Dictionary <string, List <string> > {
                            { "If-Match", new List <string> {
                                  updatedAcctETagVal
                              } }
                        }
                            );

                        // Expected program flow; this code should execute.
                        Console.WriteLine("\nAccount successfully updated using ETag: {0}.",
                                          updatedAcctETagVal);
                    }
                    catch (ServiceException e)
                    {
                        if (e.StatusCode == (int)HttpStatusCode.PreconditionFailed) // Not expected
                        {
                            Console.WriteLine("Unexpected status code: '{0}'", (int)e.StatusCode);
                        }
                        else
                        {
                            throw e;
                        }
                    }

                    // Retrieve and output current account state.
                    retrievedaccount1 = svc.Get(account1Uri.ToString() + queryOptions);

                    Console.WriteLine("\nBelow is the final state of the account");
                    Console.WriteLine(retrievedaccount1.ToString(Formatting.Indented));
                    #endregion Optimistic concurrency on delete and update

                    #region Delete created records

                    // Delete (or keep) all the created entity records.
                    Console.Write("\nDo you want these entity records deleted? (y/n) [y]: ");
                    String answer = Console.ReadLine().Trim();

                    if (!(answer.StartsWith("y") || answer.StartsWith("Y") || answer == string.Empty))
                    {
                        entityUris.Clear();
                    }

                    foreach (Uri entityUrl in entityUris)
                    {
                        svc.Delete(entityUrl);
                    }

                    #endregion Delete created records
                }
            }
            catch (ServiceException e)
            {
                Console.WriteLine("Message send response: status code {0}, {1}",
                                  e.StatusCode, e.ReasonPhrase);
            }
        }