Eksempel på Web API Basic-operationer (C#)
Udgivet: januar 2017
Gælder for: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online
Dette eksempel viser, hvordan du udfører grundlæggende CRUD (Opret, Hent, Opdater og Slet) og tilknytnings- og dissociationshandlinger på Dynamics 365-objektforekomster ved hjælp af Dynamics 365 Web API'en.
Bemærk
Dette eksempel implementerer de Dynamics 365-handlinger og det konsoloutput, der er beskrevet i Eksempel på Web API Basic operationer, og bruger de fælles C#-konstruktioner, som er beskrevet i Web API-eksempler (C#).
Dette emne indeholder
Forudsætninger
Køre dette eksempel
Kodeoversigt
Forudsætninger
Forudsætninger for alle Dynamics 365 Web API C#-eksempler, der er beskrevet i afsnittet Forudsætninger i det overordnede emne Web API-eksempler (C#).
Køre dette eksempel
Gå først til Eksempel på Microsoft CRM Web-API Basic Operations (C#), download eksempelarkivfilen, Microsoft CRM Web API Basic Operations Sample (CS).zip, og pak indholdet ud i en lokal mappe. Denne mappe skal indeholde følgende filer:
Filer |
Formål/beskrivelse |
---|---|
Program.cs |
Indeholder den primære kildekode til dette eksempel. |
App.config |
Programmets konfigurationsfil, der indeholder oplysninger pladsholder for Dynamics 365-serverforbindelsen. |
Authentication.cs |
Disse filer er placeret i mappen Web AP Helper Code og indeholder det supplerende bibliotek, der er beskrevet i Brug af Microsoft Dynamics 365 Web API Helper-bibliotek (C#). |
BasicOperations.sln |
Microsoft Visual Studio 2015-standardløsningen, -projektet, konfiguration af NuGet-pakken og filer med assemblyoplysninger for dette eksempel. |
Brug dernæst følgende procedure til at køre dette eksempel.
Find og dobbeltklik på løsningsfilen, BasicOperations.sln, for at indlæse løsningen i Visual Studio. Byg Basic Operations-løsningen. Dette bør automatisk hente og installere alle krævede NuGet-pakker, som mangler eller skal opdateres.
Rediger programmets konfigurationsfil App,config for at angive forbindelsesoplysninger for Dynamics 365-serveren. Du kan finde flere oplysninger under Web-API-hjælpekode: konfiguration af klasser.
Kør projektet BasicOperations fra Visual Studio. Alle eksempelløsninger er konfigureret til at køre i fejlfindingstilstand som standard.
Kodeoversigt
Den mest aktuelle kilde til denne fil findes i eksempeloverførselspakken.
Program.cs
using Microsoft.Crm.Sdk.Samples.HelperCode;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Text;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Linq;
using System.Net;
namespace Microsoft.Crm.Sdk.Samples
{
/// <summary>
/// This program performs basic create, retrieve, update, and delete (CRUD) and related
/// operations against a CRM Online 2016 or later organization using the Web API.
/// </summary>
/// <remarks>
/// Before building this application, you must first modify the following configuration
/// information in the app.config file:
/// - All deployments: Provide connection string service URL's for your organization.
/// - CRM Online: Replace the app settings with the correct values for your Azure
/// application registration.
/// See the provided app.config file for more information.
/// </remarks>
class BasicOperations
{
//Provides a persistent client-to-CRM server communication channel.
private HttpClient httpClient;
//Start with base version and update with actual version later.
private Version webAPIVersion = new Version(8, 0);
private string getVersionedWebAPIPath()
{
return string.Format("v{0}/", webAPIVersion.ToString(2));
}
public async Task getWebAPIVersion()
{
HttpRequestMessage RetrieveVersionRequest =
new HttpRequestMessage(HttpMethod.Get, getVersionedWebAPIPath() + "RetrieveVersion");
HttpResponseMessage RetrieveVersionResponse =
await httpClient.SendAsync(RetrieveVersionRequest);
if (RetrieveVersionResponse.StatusCode == HttpStatusCode.OK) //200
{
JObject RetrievedVersion = JsonConvert.DeserializeObject<JObject>(
await RetrieveVersionResponse.Content.ReadAsStringAsync());
//Capture the actual version available in this organization
webAPIVersion = Version.Parse((string)RetrievedVersion.GetValue("Version"));
}
else
{
Console.WriteLine("Failed to retrieve the version for reason: {0}",
RetrieveVersionResponse.ReasonPhrase);
throw new CrmHttpResponseException(RetrieveVersionResponse.Content);
}
}
//Centralized collection of entity URIs used to manage lifetimes.
List<string> entityUris = new List<string>();
//A set of variables to hold the state of and URIs for primary entity instances.
private JObject contact1 = new JObject(), contact2 = new JObject(),
retrievedContact1, retrievedContact2;
private string contact1Uri;
private JObject account1 = new JObject(), account2 = new JObject(),
retrievedAccount1, retrievedAccount2;
private string account1Uri, account2Uri;
/// <summary>
/// Demonstrates basic create, update, and retrieval operations for entity instances and
/// single properties.
/// </summary>
public async Task BasicCreateAndUpdatesAsync()
{
Console.WriteLine("--Section 1 started--");
string queryOptions; //select, expand and filter clauses
//First create a new contact instance, then add additional property values and update
// several properties.
//Local representation of CRM Contact instance
contact1.Add("firstname", "Peter");
contact1.Add("lastname", "Cambel");
HttpRequestMessage createRequest1 =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "contacts");
createRequest1.Content = new StringContent(contact1.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage createResponse1 =
await httpClient.SendAsync(createRequest1);
if (createResponse1.StatusCode == HttpStatusCode.NoContent) //204
{
Console.WriteLine("Contact '{0} {1}' created.",
contact1.GetValue("firstname"), contact1.GetValue("lastname"));
contact1Uri = createResponse1.Headers.
GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(contact1Uri);
Console.WriteLine("Contact URI: {0}", contact1Uri);
}
else
{
Console.WriteLine("Failed to create contact for reason: {0}",
createResponse1.ReasonPhrase);
throw new CrmHttpResponseException(createResponse1.Content);
}
//Add additional property values to the existing contact. As a general
// rule, only transmit a minimum working set of properties.
JObject contact1Add = new JObject();
contact1Add.Add("annualincome", 80000);
contact1Add.Add("jobtitle", "Junior Developer");
HttpRequestMessage updateRequest1 = new HttpRequestMessage(
new HttpMethod("PATCH"), contact1Uri);
updateRequest1.Content = new StringContent(contact1Add.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage updateResponse1 =
await httpClient.SendAsync(updateRequest1);
if (updateResponse1.StatusCode == HttpStatusCode.NoContent) //204
{
Console.WriteLine("Contact '{0} {1}' updated with job title" +
" and annual income.", contact1.GetValue("firstname"),
contact1.GetValue("lastname"));
}
else
{
Console.WriteLine("Failed to update contact for reason: {0}",
updateResponse1.ReasonPhrase);
throw new CrmHttpResponseException(updateResponse1.Content);
}
//Retrieve the contact with its explicitly initialized properties.
//fullname is a read-only calculated value.
queryOptions = "?$select=fullname,annualincome,jobtitle,description";
HttpResponseMessage retrieveResponse1 = await httpClient.GetAsync(
contact1Uri + queryOptions);
if (retrieveResponse1.StatusCode == HttpStatusCode.OK) //200
{
retrievedContact1 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse1.Content.ReadAsStringAsync());
Console.WriteLine("Contact '{0}' retrieved: \n\tAnnual income: {1}" +
"\n\tJob title: {2} \n\tDescription: {3}.",
// Can use either indexer or GetValue method (or a mix of two)
retrievedContact1.GetValue("fullname"),
retrievedContact1["annualincome"],
retrievedContact1["jobtitle"],
retrievedContact1["description"]); //description is initialized empty.
}
else
{
Console.WriteLine("Failed to retrieve contact for reason: {0}",
retrieveResponse1.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse1.Content);
}
//Modify specific properties and then update entity instance.
JObject contact1Update = new JObject();
contact1Update.Add("jobtitle", "Senior Developer");
contact1Update.Add("annualincome", 95000);
contact1Update.Add("description", "Assignment to-be-determined");
HttpRequestMessage updateRequest2 = new HttpRequestMessage(
new HttpMethod("PATCH"), contact1Uri);
updateRequest2.Content = new StringContent(contact1Update.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage updateResponse2 =
await httpClient.SendAsync(updateRequest2);
if (updateResponse2.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Contact '{0}' updated:", retrievedContact1["fullname"]);
Console.WriteLine("\tJob title: {0}", contact1Update["jobtitle"]);
Console.WriteLine("\tAnnual income: {0}", contact1Update["annualincome"]);
Console.WriteLine("\tDescription: {0}", contact1Update["description"]);
}
else
{
Console.WriteLine("Failed to update contact for reason: {0}",
updateResponse2.ReasonPhrase);
throw new CrmHttpResponseException(updateResponse2.Content);
}
// Change just one property
string phone1 = "555-0105";
// Create unique identifier by appending property name
string contactPhoneUri =
string.Format("{0}/{1}", contact1Uri, "telephone1");
JObject phoneValue = new JObject();
phoneValue.Add("value", phone1); //Updates must use keyword "value".
HttpRequestMessage updateRequest3 =
new HttpRequestMessage(HttpMethod.Put, contactPhoneUri);
updateRequest3.Content = new StringContent(phoneValue.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage updateResponse3 =
await httpClient.SendAsync(updateRequest3);
if (updateResponse3.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Contact '{0}' phone number updated.",
retrievedContact1["fullname"]);
}
else
{
Console.WriteLine("Failed to update the contact phone number for reason: {0}.",
updateResponse3.ReasonPhrase);
throw new CrmHttpResponseException(updateResponse3.Content);
}
//Now retrieve just the single property.
JObject retrievedProperty1;
HttpResponseMessage retrieveResponse2 =
await httpClient.GetAsync(contactPhoneUri);
if (retrieveResponse2.StatusCode == HttpStatusCode.OK)
{
retrievedProperty1 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse2.Content.ReadAsStringAsync());
Console.WriteLine("Contact's telephone# is: {0}.",
retrievedProperty1["value"]);
}
else
{
Console.WriteLine("Failed to retrieve the contact phone number for reason: {0}.",
retrieveResponse2.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse2.Content);
}
//The following capabilities require version 8.2 or higher
if (webAPIVersion >= Version.Parse("8.2"))
{
//Alternately, starting with December 2016 update (v8.2), a contact instance can be
//created and its properties returned in one operation by using a
//'Prefer: return=representation' header. Note that a 201 (Created) success status
// is returned, and not a 204 (No content).
string contactAltUri;
JObject contactAlt = new JObject();
contactAlt.Add("firstname", "Peter_Alt");
contactAlt.Add("lastname", "Cambel");
contactAlt.Add("jobtitle", "Junior Developer");
contactAlt.Add("annualincome", 80000);
contactAlt.Add("telephone1", "555-0110");
queryOptions = "?$select=fullname,annualincome,jobtitle,contactid";
HttpRequestMessage createRequestAlt =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "contacts" + queryOptions);
createRequestAlt.Content = new StringContent(contactAlt.ToString(),
Encoding.UTF8, "application/json");
createRequestAlt.Headers.Add("Prefer", "return=representation");
HttpResponseMessage createResponseAlt = await httpClient.SendAsync(createRequestAlt);
if (createResponseAlt.StatusCode == HttpStatusCode.Created) //201
{
//Body should contain the requested new-contact information.
JObject createdContactAlt = JsonConvert.DeserializeObject<JObject>(
await createResponseAlt.Content.ReadAsStringAsync());
//Because 'OData-EntityId' header not returned in a 201 response, you must instead
// construct the URI.
contactAltUri = httpClient.BaseAddress + getVersionedWebAPIPath() + "contacts(" + createdContactAlt["contactid"] + ")";
entityUris.Add(contactAltUri);
Console.WriteLine(
"Contact '{0}' created: \n\tAnnual income: {1}" + "\n\tJob title: {2}",
createdContactAlt["fullname"],
createdContactAlt["annualincome"],
createdContactAlt["jobtitle"]);
Console.WriteLine("Contact URI: {0}", contactAltUri);
}
else
{
Console.WriteLine("Failed to create contact for reason: {0}",
createResponseAlt.ReasonPhrase);
throw new CrmHttpResponseException(createResponseAlt.Content);
}
//Similarly, the December 2016 update (v8.2) also enables returning selected properties
//after an update operation (PATCH), with the 'Prefer: return=representation' header.
//Here a success is indicated by 200 (OK) instead of 204 (No content).
//Since we're reusing a local JObject, reinitialize it to remove extraneous properties.
contactAlt.RemoveAll();
contactAlt["annualincome"] = 95000;
contactAlt["jobtitle"] = "Senior Developer";
contactAlt["description"] = "MS Azure and Dynamics 365 Specialist";
queryOptions = "?$select=fullname,annualincome,jobtitle";
HttpRequestMessage updateRequestAlt = new HttpRequestMessage(
new HttpMethod("PATCH"), contactAltUri + queryOptions);
updateRequestAlt.Content = new StringContent(contactAlt.ToString(),
Encoding.UTF8, "application/json");
updateRequestAlt.Headers.Add("Prefer", "return=representation");
HttpResponseMessage updateResponseAlt = await httpClient.SendAsync(updateRequestAlt);
if (updateResponseAlt.StatusCode == HttpStatusCode.OK) //200
{
//Body should contain the requested updated-contact information.
JObject UpdatedContactAlt = JsonConvert.DeserializeObject<JObject>(
await updateResponseAlt.Content.ReadAsStringAsync());
Console.WriteLine(
"Contact '{0}' updated: \n\tAnnual income: {1}" + "\n\tJob title: {2}",
UpdatedContactAlt["fullname"],
UpdatedContactAlt["annualincome"],
UpdatedContactAlt["jobtitle"]);
}
else
{
Console.WriteLine("Failed to update contact for reason: {0}",
updateResponse1.ReasonPhrase);
throw new CrmHttpResponseException(updateResponse1.Content);
}
}
}
/// <summary>
/// Demonstrates creation of entity instance and simultaneous association to another,
/// existing entity.
/// </summary>
public async Task CreateWithAssociationAsync()
{
Console.WriteLine("\n--Section 2 started--");
string queryOptions; //select, expand and filter clauses
//Create a new account and associate with existing contact in one operation.
account1.Add("name", "Contoso Ltd");
account1.Add("telephone1", "555-5555");
account1.Add("primarycontactid@odata.bind", contact1Uri);
HttpRequestMessage createRequest2 =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "accounts");
createRequest2.Content = new StringContent(account1.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage createResponse2 =
await httpClient.SendAsync(createRequest2);
if (createResponse2.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Account '{0}' created.", account1.GetValue("name"));
account1Uri = createResponse2.Headers.GetValues("OData-EntityId").
FirstOrDefault();
entityUris.Add(account1Uri);
}
else
{
Console.WriteLine("Failed to create account for reason: {0}.",
createResponse2.ReasonPhrase);
throw new CrmHttpResponseException(createResponse2.Content);
}
//Retrieve account name and primary contact info
queryOptions =
"?$select=name,&$expand=primarycontactid($select=fullname,jobtitle,annualincome)";
HttpResponseMessage retrieveResponse3 =
await httpClient.GetAsync(account1Uri + queryOptions);
if (retrieveResponse3.StatusCode == HttpStatusCode.OK)
{
retrievedAccount1 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse3.Content.ReadAsStringAsync());
Console.WriteLine("Account '{0}' has primary contact '{1}':",
retrievedAccount1["name"],
retrievedAccount1.GetValue("primarycontactid")["fullname"]
);
Console.WriteLine("\tJob title: {0} \n\tAnnual income: {1}",
retrievedAccount1.GetValue("primarycontactid")["jobtitle"],
retrievedAccount1["primarycontactid"]["annualincome"]
);
}
else
{
Console.WriteLine("Failed to retrieve account for reason: {0}.",
retrieveResponse3.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse3.Content);
}
}
/// <summary>
/// Demonstrates creation of entity instance and related entities in a single operation.
/// </summary>
public async Task CreateRelatedAsync()
{
Console.WriteLine("\n--Section 3 started--");
string queryOptions; //select, expand and filter clauses
//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();
task1.Add("subject", "Sign invoice");
task1.Add("description", "Invoice #12321");
task1.Add("scheduledend", DateTimeOffset.Parse("4/19/2016"));
tasks.Add(task1);
JObject task2 = new JObject();
task2.Add("subject", "Setup new display");
task2.Add("description", "Theme is - Spring is in the air");
task2.Add("scheduledstart", DateTimeOffset.Parse("4/20/2016"));
tasks.Add(task2);
JObject task3 = new JObject();
task3.Add("subject", "Conduct training");
task3.Add("description", "Train team on making our new blended coffee");
task3.Add("scheduledstart", DateTimeOffset.Parse("6/1/2016"));
tasks.Add(task3);
contact2.Add("firstname", "Susie");
contact2.Add("lastname", "Curtis");
contact2.Add("jobtitle", "Coffee Master");
contact2.Add("annualincome", 48000);
//Add related tasks using corresponding navigation property
contact2.Add("Contact_Tasks", tasks);
account2.Add("name", "Fourth Coffee");
//Add related contacts using corresponding navigation property
account2.Add("primarycontactid", contact2);
HttpRequestMessage createRequest3 =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "accounts");
createRequest3.Content = new StringContent(account2.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage createResponse3 =
await httpClient.SendAsync(createRequest3);
if (createResponse3.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Account '{0}' created.",
account2.GetValue("name"));
account2Uri = createResponse3.Headers.GetValues("OData-EntityId").
FirstOrDefault();
entityUris.Add(account2Uri);
}
else
{
Console.WriteLine("Failed to create account for reason: {0}.",
createResponse3.ReasonPhrase);
throw new CrmHttpResponseException(createResponse3.Content);
}
//Retrieve account, primary contact info, and assigned tasks for contact.
//CRM only supports querying-by-expansion one level deep, so first query
// account-primary contact.
queryOptions =
"?$select=name,&$expand=primarycontactid($select=fullname,jobtitle,annualincome)";
HttpResponseMessage retrieveResponse4 =
await httpClient.GetAsync(account2Uri + queryOptions);
if (retrieveResponse4.StatusCode == HttpStatusCode.OK)
{
retrievedAccount2 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse4.Content.ReadAsStringAsync());
Console.WriteLine("Account '{0}' has primary contact '{1}':",
retrievedAccount2.GetValue("name"),
retrievedAccount2.GetValue("primarycontactid")["fullname"]);
Console.WriteLine("\tJob title: {0} \n\tAnnual income: {1}",
retrievedAccount2["primarycontactid"]["jobtitle"],
retrievedAccount2["primarycontactid"]["annualincome"]);
}
else
{
Console.WriteLine("Failed to retrieve the account/contact info for " +
"reason: {0}.", retrieveResponse4.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse4.Content);
}
//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.
string contact2Uri = string.Format("{0}contacts({1})",
httpClient.BaseAddress + getVersionedWebAPIPath(),
retrievedAccount2["primarycontactid"]["contactid"]);
entityUris.Add(contact2Uri);
queryOptions =
"?$select=fullname,&$expand=Contact_Tasks($select=subject,description,scheduledstart,scheduledend)";
HttpResponseMessage retrieveResponse5 =
await httpClient.GetAsync(contact2Uri + queryOptions);
if (retrieveResponse5.StatusCode == HttpStatusCode.OK)
{
retrievedContact2 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse5.Content.ReadAsStringAsync());
Console.WriteLine("Contact '{0}' has the following assigned tasks:",
retrievedContact2["fullname"]);
foreach (JToken tk in retrievedContact2["Contact_Tasks"])
{
Console.WriteLine(
"Subject: {0}, \n\tDescription: {1}\n\tStart: {2}\n\tEnd: {3}\n",
tk["subject"],
tk["description"],
tk["scheduledstart"].Value<DateTime>().ToString("d"),
tk["scheduledend"].Value<DateTime>().ToString("d")
);
}
}
else
{
Console.WriteLine("Failed to retrieve the contact info for reason: {0}.",
retrieveResponse5.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse5.Content);
}
}
/// <summary>
/// Demonstrates associating and disassociating of existing entity instances.
/// </summary>
public async Task AssociateExistingAsync()
{
string queryOptions; //select, expand and filter clauses
Console.WriteLine("\n--Section 4 started--");
//Add 'Peter Cambel' to the contact list of 'Fourth Coffee',
// a 1-to-N relationship.
JObject rel1 = new JObject(); //relationship object for msg content
rel1.Add("@odata.id", contact1Uri);
Uri navUri1 = new Uri(String.Format("{0}/{1}/$ref", account2Uri,
"contact_customer_accounts"));
HttpRequestMessage assocRequest1 =
new HttpRequestMessage(HttpMethod.Post, navUri1);
assocRequest1.Content = new StringContent(rel1.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage assocResponse1 =
await httpClient.SendAsync(assocRequest1);
if (assocResponse1.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Contact '{0}' associated to account '{1}'.",
retrievedContact1["fullname"], account2["name"]);
}
else
{
Console.WriteLine("Failed to associate account/contact entities " +
"for reason: {0}.", assocResponse1.ReasonPhrase);
throw new CrmHttpResponseException(assocResponse1.Content);
}
//Retrieve and output all contacts for account 'Fourth Coffee'.
JObject retrievedContactList1;
queryOptions = "?$select=fullname,jobtitle";
HttpResponseMessage retrieveResponse6 = await httpClient.GetAsync(
account2Uri + "/contact_customer_accounts" + queryOptions);
if (retrieveResponse6.StatusCode == HttpStatusCode.OK)
{
retrievedContactList1 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse6.Content.ReadAsStringAsync());
Console.WriteLine("Contact list for account '{0}':",
retrievedAccount2["name"]);
foreach (JToken ct in retrievedContactList1["value"])
{
Console.WriteLine("\tName: {0}, Job title: {1}",
ct["fullname"], ct["jobtitle"]);
}
}
else
{
Console.WriteLine("Failed to retrieve the account/contact info" +
" for reason: {0}.", retrieveResponse6.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse6.Content);
}
//Dissociate the contact from the account. For a collection-valued
// navigation property, must append URI of referenced entity.
string 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/v8.2/contacts([contactid#])/parentcustomerid_account/$ref
HttpResponseMessage disassocResponse1 =
await httpClient.DeleteAsync(dis1Uri);
if (disassocResponse1.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Contact '{0}' dissociated from account '{1}'.",
retrievedContact1["fullname"], account2["name"]);
}
else
{
Console.WriteLine("Failed to disassociate entities for reason: {0}.",
disassocResponse1.ReasonPhrase);
throw new CrmHttpResponseException(disassocResponse1.Content);
}
//Associate an opportunity to a competitor, an N-to-N relationship.
//First, create the required entity instances.
string comp1Uri, oppor1Uri;
JObject comp1 = new JObject();
comp1.Add("name", "Adventure Works");
comp1.Add("strengths",
"Strong promoter of private tours for multi-day outdoor adventures");
JObject oppor1 = new JObject();
oppor1["name"] = "River rafting adventure";
oppor1["description"] = "Sales team on a river-rafting offsite and team building";
HttpRequestMessage createRequest4 =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "competitors");
createRequest4.Content = new StringContent(comp1.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage createResponse4 =
await httpClient.SendAsync(createRequest4);
if (createResponse4.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Competitor '{0}' created.", comp1["name"]);
comp1Uri = createResponse4.Headers.GetValues("OData-EntityId").
FirstOrDefault();
entityUris.Add(comp1Uri);
}
else
{
Console.WriteLine("Failed to create competitor for reason: {0}",
createResponse4.ReasonPhrase);
throw new CrmHttpResponseException(createResponse4.Content);
}
HttpRequestMessage createRequest5 =
new HttpRequestMessage(HttpMethod.Post, getVersionedWebAPIPath() + "opportunities");
createRequest5.Content = new StringContent(oppor1.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage createResponse5 =
await httpClient.SendAsync(createRequest5);
if (createResponse5.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine("Opportunity '{0}' created.", oppor1["name"]);
oppor1Uri = createResponse5.Headers.GetValues("OData-EntityId").
FirstOrDefault();
entityUris.Add(oppor1Uri);
}
else
{
Console.WriteLine("Failed to create opportunity for reason: {0}",
createResponse5.ReasonPhrase);
throw new CrmHttpResponseException(createResponse5.Content);
}
//Associate opportunity to competitor via opportunitycompetitors_association.
// navigation property.
JObject rel2 = new JObject();
rel2.Add("@odata.id", comp1Uri);
Uri navUri2 = new Uri(String.Format("{0}/{1}/$ref", oppor1Uri,
"opportunitycompetitors_association"));
HttpRequestMessage assocRequest2 =
new HttpRequestMessage(HttpMethod.Post, navUri2);
assocRequest2.Content = new StringContent(rel2.ToString(),
Encoding.UTF8, "application/json");
HttpResponseMessage assocResponse2 =
await httpClient.SendAsync(assocRequest2);
if (assocResponse2.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine(
"Opportunity '{0}' associated with competitor '{1}'.",
oppor1["name"], comp1["name"]);
}
else
{
Console.WriteLine("Failed to associate competitor/opportunity" +
"entities for reason: {0}.", assocResponse2.ReasonPhrase);
throw new CrmHttpResponseException(assocResponse2.Content);
}
//Retrieve all opportunities for competitor 'Adventure Works'.
JObject retrievedOpporList1;
//Select only 'name' to limit returned competitor properties.
queryOptions =
"?$select=name,&$expand=opportunitycompetitors_association($select=name,description)";
HttpResponseMessage retrieveResponse7 =
await httpClient.GetAsync(comp1Uri + queryOptions);
if (retrieveResponse7.StatusCode == HttpStatusCode.OK)
{
retrievedOpporList1 = JsonConvert.DeserializeObject<JObject>(
await retrieveResponse7.Content.ReadAsStringAsync());
Console.WriteLine("Competitor '{0}' has the following opportunities:",
retrievedOpporList1["name"]);
foreach (JToken op in
retrievedOpporList1["opportunitycompetitors_association"])
{
Console.WriteLine("\tName: {0}, \n\tDescription: {1}",
op["name"], op["description"]);
}
}
else
{
Console.WriteLine("Failed to retrieve the competitor/opportunity"
+ " info for reason: {0}.",
retrieveResponse7.ReasonPhrase);
throw new CrmHttpResponseException(retrieveResponse7.Content);
}
//Dissociate opportunity from competitor.
string dis2Uri = navUri2 + "?$id=" + comp1Uri;
HttpResponseMessage disassocResponse2 =
await httpClient.DeleteAsync(dis2Uri);
if (disassocResponse2.StatusCode == HttpStatusCode.NoContent)
{
Console.WriteLine(
"Opportunity '{0}' disassociated from competitor '{1}'.",
oppor1["name"], comp1["name"]);
}
else
{
Console.WriteLine("Failed to disassociate entities for reason: {0}.",
disassocResponse1.ReasonPhrase);
throw new CrmHttpResponseException(disassocResponse1.Content);
}
}
/// <summary>
///Provides the high-level logical flow of the program, as well as a section
/// containing cleanup code.
/// </summary>
public async Task RunAsync()
{
try
{
await getWebAPIVersion();
await BasicCreateAndUpdatesAsync();
await CreateWithAssociationAsync();
await CreateRelatedAsync();
await AssociateExistingAsync();
}
catch (Exception ex)
{ DisplayException(ex); }
finally
{
#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.
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(); }
HttpResponseMessage deleteResponse1;
int successCnt = 0, notFoundCnt = 0, failCnt = 0;
HttpContent lastBadResponseContent = null;
foreach (string ent in entityUris)
{
deleteResponse1 = await httpClient.DeleteAsync(ent);
if (deleteResponse1.IsSuccessStatusCode) //200-299
{
Console.WriteLine("Entity deleted: \n{0}.", ent);
successCnt++;
}
else if (deleteResponse1.StatusCode == HttpStatusCode.NotFound) //404
{
//May have been deleted by another user or via cascade operation
Console.WriteLine("Entity not found: {0}.", ent);
notFoundCnt++;
}
else
{
Console.WriteLine("Failed to delete: {0}.", ent);
failCnt++;
lastBadResponseContent = deleteResponse1.Content;
}
}
Console.WriteLine("Entities deleted: {0}, not found: {1}, delete " +
"failures: {2}. \n", successCnt, notFoundCnt, failCnt);
entityUris.Clear();
if (failCnt > 0)
{
//Throw last failure
throw new CrmHttpResponseException(lastBadResponseContent);
}
#endregion Section 5: Delete sample entities
}
}
static public void Main(string[] args)
{
BasicOperations app = new BasicOperations();
try
{
//Read configuration file and connect to specified CRM server.
app.ConnectToCRM(args);
Task.WaitAll(Task.Run(async () => await app.RunAsync()));
}
catch (System.Exception ex) { DisplayException(ex); }
finally
{
if (app.httpClient != null)
{ app.httpClient.Dispose(); }
Console.WriteLine("Press <Enter> to exit the program.");
Console.ReadLine();
}
}
/// <summary>
/// Obtains the connection information from the application's configuration file, then
/// uses this info to connect to the specified CRM service.
/// </summary>
/// <param name="args"> Command line arguments. The first specifies the name of the
/// connection string setting. </param>
private void ConnectToCRM(String[] cmdargs)
{
//Create a helper object to read app.config for service URL and application
// registration settings.
Configuration config = null;
if (cmdargs.Length > 0)
{ config = new FileConfiguration(cmdargs[0]); }
else
{ config = new FileConfiguration(null); }
//Create a helper object to authenticate the user with this connection info.
Authentication auth = new Authentication(config);
//Next use a HttpClient object to connect to specified CRM Web service.
httpClient = new HttpClient(auth.ClientHandler, true);
//Define the Web API base address, the max period of execute time, the
// default OData version, and the default response payload format.
httpClient.BaseAddress = new Uri(config.ServiceUrl + "api/data/");
httpClient.Timeout = new TimeSpan(0, 2, 0);
httpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
httpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
/// <summary> Helper method to display caught exceptions </summary>
private static void DisplayException(Exception ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine(ex.Message);
while (ex.InnerException != null)
{
Console.WriteLine("\t* {0}", ex.InnerException.Message);
ex = ex.InnerException;
}
}
}
}
Se også
Brug Microsoft Dynamics 365 Web API
Oprette et objekt ved hjælp af Web-API
Opdatere og slette objekter ved hjælp af web-API'et
Hente et objekt ved hjælp af web-API'et
Opdatere og slette objekter ved hjælp af web-API'et
Web API-eksempler
Eksempel på Web API Basic operationer
Eksempel på Web API Basic operationer (JavaScript på klientsiden)
Eksempel på Web API-forespørgselsdata (C#)
Eksempel på Web API Conditional operationer (C#)
Web API-funktioner og handlingseksempel (C#)
Microsoft Dynamics 365
© 2017 Microsoft. Alle rettigheder forbeholdes. Ophavsret