웹 API 함수 및 동작 샘플(C#)
게시 날짜: 2017년 1월
적용 대상: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online
이 샘플은 Dynamics 365 웹 API를 사용하여 사용자 지정 작업을 포함하는 바운드 및 언바운드 함수와 동작을 호출하는 방법을 기술합니다.
참고
이 샘플은 웹 API 함수 및 동작 샘플에서 자세히 설명된 작업을 구현하며 웹 API 샘플(C#)에 설명된 공통 클라이언트 쪽 C# 작성을 사용합니다.
이 항목의 내용
필수 조건
샘플 실행
코드 목록
필수 조건
모든 Dynamics 365 웹 API C# 샘플의 필수 조건은 상위 항목 웹 API 샘플(C#)의 66e26684-819e-45f7-bec4-c250be4d6fed#bkmk_prerequisites 섹션에 자세히 설명되어 있습니다.
샘플 실행
먼저 Microsoft CRM 웹 API 함수 및 동작 샘플(C#)로 이동하여 샘플 아카이브 파일(Microsoft CRM Web API Functions and Actions Sample (CS).zip)을 다운로드하고 로컬 폴더에 압축을 풉니다. 이 폴더는 다음 파일을 포함합니다.
File |
목적/설명 |
---|---|
Program.cs |
이 샘플에 대한 기본 소스 코드를 포함합니다. |
App.config |
자리 표시자 Dynamics 365 서버 연결 정보를 포함하는 응용 프로그램 구성 파일입니다. |
Authentication.cs |
웹 API 도우미 코드 폴더에 있는 이 파일들은 Microsoft Dynamics 365 웹 API 도우미 라이브러리(C#) 사용에 자세히 설명된 추가 라이브러리를 구성합니다. |
FunctionsAndActions.sln |
이 샘플에 대한 표준 Microsoft Visual Studio 2015 솔루션, 프로젝트, NuGet 패키지 구성 및 어셈블리 정보 파일입니다. |
WebAPIFunctionsandActions_1_0_0_0_managed.zip |
이 샘플에서 호출되는 두 개의 사용자 지정 동작을 포함하는 사용자 지정 관리형 솔루션입니다. |
다음 절차에 따라 이 샘플을 실행하십시오.
솔루션 파일(FunctionsAndActions.sln)을 찾아 두 번 클릭하여 Visual Studio에 솔루션을 로드합니다.FunctionsAndActions 솔루션을 빌드합니다. 누락되었거나 업데이트되어야 하는 모든 필요한 NuGet 패키지가 자동으로 다운로드되고 설치됩니다.
응용 프로그램 구성 파일 App.config를 편집하여 Dynamics 365 서버 연결 정보를 지정합니다. 자세한 내용은 웹 API 도우미 코드: 구성 클래스을 참조하십시오.
Visual Studio 내에서 FunctionsAndActions 프로젝트를 실행합니다. 모든 샘플 솔루션은 기본적으로 디버그 모드에서 실행되도록 구성됩니다.
코드 목록
이 파일에 대한 최신 소스는 샘플 다운로드 패키지에 있습니다.
Program.cs
using Microsoft.Crm.Sdk.Samples.HelperCode;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
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 demonstrates calling bound and unbound
/// functions and actions using the CRM Web API. </summary>
/// <remarks>
/// The following standard CRM Web API functions and actions are invoked:
/// - WhoAmI, a basic unbound function
/// - GetTimeZoneCodeByLocalizedName, an unbound function that requires parameters
/// - CalculateTotalTimeIncident, a bound function
/// - WinOpportunity, an unbound action that takes parameters
/// - AddToQueue, a bound action that takes parameters
/// In addition, a managed solution is loaded and custom bound and an unbound actions
/// contained within are invoked.
///
/// TODO: 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 FunctionsAndActions
{
//Provides a persistent client-to-CRM server communication channel.
HttpClient httpClient;
//Associated solution package that contains custom actions used by this sample
const string customSolutionFilename = "WebAPIFunctionsandActions_1_0_0_0_managed.zip";
const string customSolutionName = "WebAPIFunctionsandActions";
string customSolutionID = null;
//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.
string incident1Uri, opportunity1Uri, contact1Uri;
//Objects associated with created URIs, where required.
JObject contact1;
Guid myUserId; //CRM User ID for current user.
/// <summary> Principal sample code that demonstrates calling bound and unbound functions
/// and actions.</summary>
public async Task RunAsync()
{
HttpResponseMessage response = null;
#region Call an unbound function with no parameters.
//Retrieve the current user's full name from the WhoAmI function:
// https://msdn.microsoft.com/library/mt607925.aspx, which returns a WhoAmIResponse
// complex type: https://msdn.microsoft.com/library/mt607982.aspx.
string currentUser;
Console.WriteLine("Unbound function: WhoAmI");
response = await httpClient.GetAsync("WhoAmI", HttpCompletionOption.ResponseContentRead);
if (!response.IsSuccessStatusCode)
{ throw new CrmHttpResponseException(response.Content); }
JObject whoAmIresp = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
//First obtain the user's ID.
myUserId = (Guid)whoAmIresp["UserId"];
//Then retrieve the full name for that unique ID.
string requestUri = "systemusers(" + myUserId + ")?$select=fullname";
response = await httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
JObject user = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
currentUser = user["fullname"].ToString();
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{ currentUser = "[not registered]"; }
else
{
Console.WriteLine("Error calling WhoAmI!");
throw new CrmHttpResponseException(response.Content);
}
Console.WriteLine("\tCurrent user has system name '{0}'.", currentUser);
#endregion Call an unbound function with no parameters.
#region Call an unbound function that requires parameters.
//Retrieve the time zone code for the specified time zone, using the GetTimeZoneCodeByLocalizedName
//function: https://msdn.microsoft.com/library/mt607644.aspx, which returns a GetTimeZoneCodeBy-
//LocalizedNameResponse complex type: https://msdn.microsoft.com/library/mt607889.aspx.
string timeZoneCode;
int localeID = 1033;
string timeZoneName = "Pacific Standard Time";
JObject LocalizedNameResponse;
//Demonstrates best practice of passing 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 + ")"];
Console.WriteLine("Unbound function: GetTimeZoneCodeByLocalizedName");
response = await httpClient.GetAsync(string.Join("", uria),
HttpCompletionOption.ResponseHeadersRead);
if (response.StatusCode == HttpStatusCode.OK)
{
LocalizedNameResponse = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
}
else
{
Console.WriteLine("Error calling GetTimeZoneCodeByLocalizedName!");
throw new CrmHttpResponseException(response.Content);
}
timeZoneCode = LocalizedNameResponse["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.
//Retrieve the total time, in minutes, spent on all tasks associated with an incident.
//Uses the CalculateTotalTimeIncident function: https://msdn.microsoft.com/library/mt593054.aspx,
//which returns a CalculateTotalTimeIncidentResponse complex type:
//https://msdn.microsoft.com/library/mt607924.aspx.
string totalTime;
string boundUri = incident1Uri + @"/Microsoft.Dynamics.CRM.CalculateTotalTimeIncident()";
Console.WriteLine("Bound function: CalculateTotalTimeIncident");
response = await httpClient.GetAsync(boundUri, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
JObject cttir = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
totalTime = cttir["TotalTime"].ToString();
}
else
{
Console.WriteLine("Error calling CalculateTotalTimeIncident!");
throw new CrmHttpResponseException(response.Content);
}
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.
//Close an opportunity and marks it as won. Uses the WinOpportunity action:
//https://msdn.microsoft.com/library/mt607971.aspx,
//which takes a int32 status code and an opportunityclose entity type:
//https://msdn.microsoft.com/library/mt593099.aspx.
JObject opportClose = new JObject();
opportClose["subject"] = "Won Opportunity";
opportClose["opportunityid@odata.bind"] = opportunity1Uri;
JObject winOpportParams = new JObject();
winOpportParams["Status"] = 3;
winOpportParams["OpportunityClose"] = opportClose;
Console.WriteLine("Unbound action: WinOpportunity");
response = await SendAsJsonAsync(httpClient, HttpMethod.Post, "WinOpportunity",
winOpportParams);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine("Error calling WinOpportunity!");
throw new CrmHttpResponseException(response.Content);
}
Console.WriteLine("\tOpportunity won.");
#endregion Call an unbound action that requires parameters.
#region Call a bound action that requires parameters.
//Add a new letter tracking activity to the current user's queue. Uses the AddToQueue
//action: https://msdn.microsoft.com/library/mt607880.aspx, which is bound to the queue
//entity type: https://msdn.microsoft.com/library/mt607886.aspx, and returns a
//AddToQueueResponse complex type: https://msdn.microsoft.com/en-us/library/mt608105.aspx.
string queueItemId;
//Create a letter tracking instance.
string letterUri;
JObject letter = new JObject();
letter["description"] = "Example letter";
Console.WriteLine("Bound action: AddToQueue");
response = await SendAsJsonAsync(httpClient, HttpMethod.Post, "letters", letter);
if (response.StatusCode == HttpStatusCode.NoContent)
{
letterUri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(letterUri);
}
else
{
Console.WriteLine("Error creating tracking letter!");
throw new CrmHttpResponseException(response.Content);
}
//Retrieve the ID associated with this new letter tracking activity.
string letterActivityId;
response = await httpClient.GetAsync(letterUri + "?$select=activityid",
HttpCompletionOption.ResponseContentRead);
if (response.IsSuccessStatusCode)
{
JObject letterRetreived = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
letterActivityId = letterRetreived["activityid"].ToString();
}
else
{
Console.WriteLine("Error retrieving tracking letter activity ID!");
throw new CrmHttpResponseException(response.Content);
}
//Retrieve URL to current user's queue.
string myQueueUri;
response = await httpClient.GetAsync("systemusers(" + myUserId + ")/queueid/$ref",
HttpCompletionOption.ResponseContentRead);
if (response.IsSuccessStatusCode)
{
JObject queueRef = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
myQueueUri = queueRef["@odata.id"].ToString();
}
else
{
Console.WriteLine("Error retrieving current user queue URL!");
throw new CrmHttpResponseException(response.Content);
}
//Add letter activity to current user's queue, then return its queue ID.
JObject addToQueueParams = new JObject();
addToQueueParams["Target"] = JObject.Parse(
@"{activityid: '" + letterActivityId + @"', '@odata.type': 'Microsoft.Dynamics.CRM.letter' }");
response = await SendAsJsonAsync(httpClient, HttpMethod.Post,
myQueueUri + "/Microsoft.Dynamics.CRM.AddToQueue", addToQueueParams);
if (response.StatusCode == HttpStatusCode.OK)
{
JObject queueResponse = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
queueItemId = queueResponse["QueueItemId"].ToString();
}
else
{
Console.WriteLine("Error adding letter activity to queue!");
throw new CrmHttpResponseException(response.Content);
}
Console.WriteLine("\tQueueItemId returned from AddToQueue action: {0}", queueItemId);
#endregion Call a bound action that requires parameters.
//Attempt to load the associated managed solution so that we can call its custom actions.
await InstallCustomSolutionAsync(true);
if (customSolutionID == null)
{
Console.WriteLine("Failed to install custom solution, so custom operations cannot be called.");
return;
}
#region 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.
string annote1Url;
JObject note1 = JObject.Parse(
@"{NoteTitle: 'Note Title', NoteText: 'The text content of the note.'}");
string actionUri = contact1Uri + "/Microsoft.Dynamics.CRM.sample_AddNoteToContact";
Console.WriteLine("Custom action: sample_AddNoteToContact");
response = await SendAsJsonAsync(httpClient, HttpMethod.Post, actionUri, note1);
if (response.StatusCode == HttpStatusCode.OK)
{
JObject contact = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
annote1Url = httpClient.BaseAddress + "/annotations(" + contact["annotationid"] + ")";
}
else
{
Console.WriteLine("Error calling custom action sample_AddNoteToContact!");
throw new CrmHttpResponseException(response.Content);
}
Console.WriteLine("\tA note with the title '{0}' was created and " +
"associated with the contact {2}.",
note1["NoteTitle"], note1["NoteText"],
contact1["firstname"] + " '" + contact1["lastname"] + "'");
#endregion Call a bound custom action that requires parameters.
#region Call an unbound custom action that requires parameters.
//Create a customer of the specified type, using the custom action sample_CreateCustomer,
//which takes two prams: the type of customer ('account' or 'contact') and the name of
//the new customer.
string customerName1 = "New account customer (sample)";
JObject customerParam = JObject.Parse(
@"{CustomerType: 'account', AccountName: '" + customerName1 + "'}");
Console.WriteLine("Custom action: sample_CreateCustomer");
response = await SendAsJsonAsync(httpClient, HttpMethod.Post,
"sample_CreateCustomer", customerParam);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine("Error calling custom action sample_CreateCustomer!");
throw new CrmHttpResponseException(response.Content);
}
Console.WriteLine("\tThe account '" + customerName1 + "' was created.");
//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.)
customerParam = JObject.Parse(
@"{CustomerType: 'contact', AccountName: '" + customerName1 + "'}");
response = await SendAsJsonAsync(httpClient, HttpMethod.Post,
"sample_CreateCustomer", customerParam);
if (response.IsSuccessStatusCode)
{ Console.WriteLine("\tCall to CreateCustomer not expected to succeed."); }
else
{
CrmHttpResponseException ex = new CrmHttpResponseException(response.Content);
Console.WriteLine("\tExpected Error: " + ex.Message);
}
#endregion Call an unbound custom action that requires parameters.
}
/// <summary> Retrieves the solution ID associated with a solution name </summary>
/// <param name="solutionName">Unique name of the solution (uniquename)</param>
/// <returns>The solutionid property for this solution if found; otherwise null.</returns>
private async Task<string> GetSolutionIDAsync(string solutionName)
{
HttpResponseMessage response;
string solutionID = null;
if (String.IsNullOrEmpty(solutionName)) { return null; }
string queryOptions = "solutions?$select=solutionid&$filter=uniquename eq '" + solutionName + "'";
response = await httpClient.GetAsync(queryOptions, HttpCompletionOption.ResponseHeadersRead);
if (response.StatusCode == HttpStatusCode.OK)
{
JObject solutionArray = JsonConvert.DeserializeObject<JObject>(await
response.Content.ReadAsStringAsync());
//There can only be zero or one returned record when filtering on a unique property.
if (!solutionArray["value"].Any())
{ solutionID = null; }
else
{ solutionID = solutionArray["value"].First()["solutionid"].ToString(); }
}
else
{ throw new CrmHttpResponseException(response.Content); }
return solutionID;
}
/// <summary> Installs (or uninstalls) the custom, managed solution associated with
/// this sample </summary>
/// <remarks>Uses the ImportSolution action: https://msdn.microsoft.com/library/mt608117.aspx
/// </remarks>
/// <param name="install">true to install (default); otherwise, false to uninstall</param>
/// <returns>void</returns>
private async Task InstallCustomSolutionAsync(bool install = true)
{
HttpResponseMessage response;
//Check first if the solution is already installed by retrieving its ID.
customSolutionID = await GetSolutionIDAsync(customSolutionName);
//Request to install and solution is not already present
if (install == true && customSolutionID == null)
{
//Locate the custom solution zip file, which should have been copied over to the build
//output directory.
string solutionPath = Directory.GetCurrentDirectory() + "\\" + customSolutionFilename;
if (!File.Exists(solutionPath))
{ return; }
//Read the solution package into memory
byte[] packageBytes = File.ReadAllBytes(solutionPath);
//Import the solution package.
JObject importParams = new JObject();
importParams["CustomizationFile"] = packageBytes;
importParams["OverwriteUnmanagedCustomizations"] = false;
importParams["PublishWorkflows"] = false;
importParams["ImportJobId"] = Guid.NewGuid();
response = await SendAsJsonAsync(httpClient, HttpMethod.Post, "ImportSolution", importParams);
if (response.IsSuccessStatusCode)
{
customSolutionID = await GetSolutionIDAsync(customSolutionName);
string solutionUri = httpClient.BaseAddress.ToString() + "solutions(" + customSolutionID + ")";
entityUris.Add(solutionUri); //Add to lifetime-managed records.
return;
}
else
{ throw new CrmHttpResponseException(response.Content); }
}
//Request to uninstall and solution is present.
else if (install == false && customSolutionID != null)
{
string solutionUri = httpClient.BaseAddress.ToString() + "solutions(" + customSolutionID + ")";
response = httpClient.DeleteAsync(solutionUri).Result;
customSolutionID = null;
}
}
static void Main(string[] args)
{
FunctionsAndActions app = new FunctionsAndActions();
try
{
//Read configuration file and connect to specified CRM server.
app.ConnectToCRM(args);
app.CreateRequiredRecords();
Task.WaitAll(Task.Run(async () => await app.RunAsync()));
}
catch (System.Exception ex) { DisplayException(ex);
}
finally
{
if (app.httpClient != null)
{
app.DeleteRequiredRecords(true);
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.
FileConfiguration 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/v8.1/");
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> Creates the CRM entity instances used by this sample. </summary>
private void CreateRequiredRecords()
{
//Create a parent account, an associated incident with three associated tasks
//(required for CalculateTotalTimeIncident).
JObject account1 = new JObject();
string account1Uri;
account1["name"] = "Fourth Coffee";
HttpResponseMessage response = SendAsJsonAsync(httpClient, HttpMethod.Post,
"accounts", account1).Result;
if (response.StatusCode == HttpStatusCode.NoContent)
{
account1Uri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(account1Uri);
}
else
{ throw new CrmHttpResponseException(response.Content); }
JArray tasks = 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 incident1 = new JObject();
incident1["title"] = "Sample Case";
incident1["customerid_account@odata.bind"] = account1Uri;
incident1["Incident_Tasks"] = tasks;
response = SendAsJsonAsync(httpClient, HttpMethod.Post, "incidents", incident1).Result;
if (response.StatusCode == HttpStatusCode.NoContent)
{
incident1Uri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(incident1Uri);
}
else
{ throw new CrmHttpResponseException(response.Content); }
//Close the associated tasks (so that they represent completed work).
JObject incidentTaskRefs;
//Property value set used to mark tasks as completed.
JObject completeCode = JObject.Parse(@"{statecode: 1, statuscode: 5}");
response = httpClient.GetAsync(incident1Uri + "/Incident_Tasks/$ref",
HttpCompletionOption.ResponseContentRead).Result;
if (response.IsSuccessStatusCode)
{
incidentTaskRefs = JsonConvert.DeserializeObject<JObject>
(response.Content.ReadAsStringAsync().Result);
}
else
{ throw new CrmHttpResponseException(response.Content); }
foreach (var taskRef in incidentTaskRefs["value"])
{
response = SendAsJsonAsync(httpClient, new HttpMethod("PATCH"),
taskRef["@odata.id"].ToString(), completeCode).Result;
if (!response.IsSuccessStatusCode)
{ throw new CrmHttpResponseException(response.Content); }
}
//Create another account and associated opportunity (required for CloseOpportunityAsWon).
JObject account2 = new JObject();
string account2Uri;
account2["name"] = "Coho Winery";
account2["opportunity_customer_accounts"] = JArray.Parse(@"[{ name: 'Opportunity to win' }]");
response = SendAsJsonAsync(httpClient, HttpMethod.Post, "accounts", account2).Result;
if (response.StatusCode == HttpStatusCode.NoContent)
{
account2Uri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(account2Uri);
}
else
{ throw new CrmHttpResponseException(response.Content); }
//Retrieve the URI to the opportunity.
JObject custOpporRefs;
response = httpClient.GetAsync(account2Uri + "/opportunity_customer_accounts/$ref",
HttpCompletionOption.ResponseContentRead).Result;
if (response.IsSuccessStatusCode)
{
custOpporRefs = JsonConvert.DeserializeObject<JObject>
(response.Content.ReadAsStringAsync().Result);
opportunity1Uri = custOpporRefs["value"][0]["@odata.id"].ToString();
}
else
{ throw new CrmHttpResponseException(response.Content); }
//Create a contact to use with custom action sample_AddNoteToContact
contact1 = JObject.Parse(@"{firstname: 'Jon', lastname: 'Fogg'}");
response = SendAsJsonAsync(httpClient, HttpMethod.Post, "contacts", contact1).Result;
if (response.StatusCode == HttpStatusCode.NoContent)
{
contact1Uri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
entityUris.Add(contact1Uri);
}
else
{ throw new CrmHttpResponseException(response.Content); }
}
/// <summary> Deletes the CRM entity instance sample data created by this sample. </summary>
/// <param name="prompt">True to prompt the user for confirmation and display results;
/// otherwise False to execute silently.</param>
/// <returns>Number of entity instances deleted</returns>
private int DeleteRequiredRecords(bool prompt)
{
if (entityUris.Count == 0) return 0;
if (prompt)
{
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 0; }
}
HttpResponseMessage response;
int successCnt = 0, failCnt = 0;
HttpContent lastBadResponseContent = null;
foreach (string ent in entityUris)
{
response = httpClient.DeleteAsync(ent).Result;
if (response.IsSuccessStatusCode)
{ successCnt++; }
else if (response.StatusCode == HttpStatusCode.NotFound)
{; } //Entity may have been deleted by another user or via cascade delete.
else //Failed to delete
{
failCnt++;
lastBadResponseContent = response.Content;
}
}
entityUris.Clear();
if (failCnt > 0)
{
//Throw last failure.
throw new CrmHttpResponseException(lastBadResponseContent);
}
if (prompt)
{ Console.WriteLine("Deleted {0} records!", successCnt); }
return successCnt;
}
/// <summary> Sends an HTTP message containing a JSON payload to the target URL. </summary>
/// <typeparam name="T">Type of the data to send in the message content (payload)</typeparam>
/// <param name="client">A preconfigured HTTP client</param>
/// <param name="method">The HTTP method to invoke</param>
/// <param name="requestUri">The relative URL of the message request</param>
/// <param name="value">The data to send in the payload. The data will be converted to a
/// serialized JSON payload. </param>
/// <returns>An HTTP response message</returns>
private async Task<HttpResponseMessage> SendAsJsonAsync<T>(HttpClient client,
HttpMethod method, string requestUri, T value)
{
string content;
if (value.GetType().Name.Equals("JObject"))
{ content = value.ToString(); }
else
{
content = JsonConvert.SerializeObject(value, new JsonSerializerSettings()
{ DefaultValueHandling = DefaultValueHandling.Ignore });
}
HttpRequestMessage request = new HttpRequestMessage(method, requestUri);
request.Content = new StringContent(content);
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
return await client.SendAsync(request);
}
/// <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;
}
}
}
}
참고 항목
Microsoft Dynamics 365 웹 API 사용
웹 API 기능 사용
웹 API 작업 사용
웹 API 샘플
웹 API 함수 및 동작 샘플
웹 API 함수 및 동작 샘플(클라이언트 쪽 JavaScript)
웹 API 기본 작업 샘플(C#)
웹 API 쿼리 데이터 샘플(C#)
웹 API 조건부 작업 샘플(C#)
Microsoft Dynamics 365
© 2017 Microsoft. All rights reserved. 저작권 정보