Dynamics 365/CRM OData Webapi 基础扩展,以下为基本使用方式,更多能力请自动探索
var mscrmConfig = AppSettingsExt.Get<MscrmConfig>("MscrmConfig");
using var client = ODataExt.GetClient(mscrmConfig);
client.Create(entity.SetName, ppentity.ToJsonEntity());
以下为ODataExt.cs源代码,欢迎大家指指点点,感谢大家的点赞与支持
/* file name:lce.mscrm.engine.ODataExt.cs
* author:lynx lynx.kor@163.com @ 2021/7/3 13:42:48
* copyright (c) 2021 Copyright@lynxce.com
* desc:
* > add description for ODataExt
* revision:
*
*/
using leap.mscrm.engine;
using Newtonsoft.Json.Linq;
using System.Net;
using System.Net.Http.Headers;
using lce.ext.providers;
using System.Text.RegularExpressions;
namespace lce.mscrm.engine
{
/// <summary>
/// action:ODataExt
/// </summary>
public static class ODataExt
{
private static readonly TimeSpan DEFAULT_TIMESPAN = TimeSpan.FromMinutes(2);
/// <summary>
/// Clones a HttpRequestMessage instance
/// </summary>
/// <param name="request">The HttpRequestMessage to clone.</param>
/// <returns>A copy of the HttpRequestMessage</returns>
public static HttpRequestMessage Clone(this HttpRequestMessage request)
{
var clone = new HttpRequestMessage(request.Method, request.RequestUri)
{
Content = request.Content.Clone(),
Version = request.Version
};
foreach (KeyValuePair<string, object> prop in request.Properties)
{
clone.Properties.Add(prop);
}
foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
{
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return clone;
}
/// <summary>
/// Clones a HttpContent instance
/// </summary>
/// <param name="content">The HttpContent to clone</param>
/// <returns>A copy of the HttpContent</returns>
public static HttpContent Clone(this HttpContent content)
{
if (content == null) return null;
HttpContent clone;
switch (content)
{
case StringContent sc:
clone = new StringContent(sc.ReadAsStringAsync().Result);
break;
default:
throw new Exception($"{content.GetType()} Content type not implemented for HttpContent.Clone extension method.");
}
clone.Headers.Clear();
foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers)
{
clone.Headers.Add(header.Key, header.Value);
}
return clone;
}
/// <summary>
/// Creates an entity and returns the id
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName"></param>
/// <param name="body"> </param>
/// <returns></returns>
public static Guid Create(this HttpClient client, string entitySetName, object body)
{
var uri = client.PostCreate(entitySetName, body);
return Guid.Parse(Regex.Replace(uri.AbsoluteUri, @"(.*\()(.*)(\).*)", "$2"));
}
/// <summary>
/// Deletes an entity
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource to delete</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
public static void Delete(this HttpClient client, string path, Dictionary<string, List<string>> headers = null)
{
client.DeleteAsync(path, headers).GetAwaiter().GetResult();
}
/// <summary>
/// 删除记录
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName"></param>
/// <param name="id"> </param>
/// <returns></returns>
public static void Delete(this HttpClient client, string entitySetName, Guid id)
{
try
{
var path = $"{client.BaseAddress.AbsoluteUri.TrimEnd('/')}/{entitySetName}({id})";
client.DeleteAsync(path, null).GetAwaiter().GetResult();
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 删除字段值
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName"></param>
/// <param name="id"> </param>
/// <param name="property"> </param>
public static void Delete(this HttpClient client, string entitySetName, Guid id, string property)
{
var path = $"{client.BaseAddress.AbsoluteUri.TrimEnd('/')}/{entitySetName}({id})/{property}";
client.DeleteAsync(path, null).GetAwaiter().GetResult();
}
/// <summary>
/// 删除记录
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName"></param>
/// <param name="id"> </param>
/// <returns></returns>
public static async Task DeleteAsync(this HttpClient client, string entitySetName, Guid id)
{
try
{
var path = $"{client.BaseAddress.AbsoluteUri.TrimEnd('/')}/{entitySetName}({id})";
client.DeleteAsync(path, null).GetAwaiter().GetResult();
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Deletes an entity asychronously
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource to delete.</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
/// <returns>Task</returns>
public static async Task DeleteAsync(this HttpClient client, string path, Dictionary<string, List<string>> headers = null)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Delete, new Uri(path)))
{
if (headers != null)
{
foreach (KeyValuePair<string, List<string>> header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
using (HttpResponseMessage response = await SendAsync(client, message))
response.Dispose();
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 单条数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="entityName">实体名</param>
/// <param name="conditions">查询条件</param>
/// <param name="columns"> 查询字段</param>
/// <returns></returns>
public static T Get<T>(this HttpClient client, string entityName, IList<ConditionItem> conditions, IList<string> columns, IList<OrderItem> orders = null) where T : TEntity
{
var fetchXml = FetchXmlExt.FetchXml(entityName, columns, conditions, orders);
var result = Activator.CreateInstance<T>();
return client.Get<T>(result.SetName, fetchXml);
}
/// <summary>
/// 单条数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="entitySetName">实体集合名</param>
/// <param name="entityName"> 实体名</param>
/// <param name="conditions"> 查询条件</param>
/// <param name="columns"> 查询字段</param>
/// <returns></returns>
public static T Get<T>(this HttpClient client, string entitySetName, string entityName, IList<ConditionItem> conditions, IList<string> columns, IList<OrderItem> orders = null) where T : TEntity
{
var fetchXml = FetchXmlExt.FetchXml(entityName, columns, conditions, orders);
return client.Get<T>(entitySetName, fetchXml);
}
/// <summary>
/// 单条数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="entitySetName">实体集合名</param>
/// <param name="fetchXml"> fetchXml</param>
/// <returns></returns>
public static T Get<T>(this HttpClient client, string entitySetName, string fetchXml) where T : TEntity
{
var path = $"{entitySetName}?fetchXml={fetchXml}";
var entity = client.Get(path);
return entity.JsonToModel<T>();
}
/// <summary>
/// Gets a typed response from a specified resource.
/// </summary>
/// <typeparam name="T">The type of response</typeparam>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource.</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
/// <returns>The typed response from the request.</returns>
public static T Get<T>(this HttpClient client, string path, Dictionary<string, List<string>> headers = null)
{
return client.GetAsync<T>(path, headers).GetAwaiter().GetResult();
}
/// <summary>
/// Retrieves data from a specified resource.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
/// <returns>The response from the request.</returns>
public static JToken Get(this HttpClient client, string path, Dictionary<string, List<string>> headers = null)
{
return client.GetAsync(path, headers).GetAwaiter().GetResult(); ;
}
/// <summary>
/// Gets a typed response from a specified resource asychronously
/// </summary>
/// <typeparam name="T">The type of resource</typeparam>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource.</param>
/// <param name="headers"></param>
/// <returns>Any custom headers to control optional behaviors.</returns>
public static async Task<T> GetAsync<T>(this HttpClient client, string path, Dictionary<string, List<string>> headers = null)
{
return (await client.GetAsync(path, headers)).ToObject<T>();
}
/// <summary>
/// Retrieves data from a specified resource asychronously.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource.</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
/// <returns>The response to the request.</returns>
public static async Task<JToken> GetAsync(this HttpClient client, string path, Dictionary<string, List<string>> headers = null)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Get, path))
{
if (headers != null)
{
foreach (KeyValuePair<string, List<string>> header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
using (HttpResponseMessage response = await SendAsync(client, message, HttpCompletionOption.ResponseContentRead))
{
if (response.StatusCode != HttpStatusCode.NotModified)
{
return JToken.Parse(await response.Content.ReadAsStringAsync());
}
}
return null;
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Get OData HttpClient
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
public static HttpClient GetClient(MscrmConfig config)
{
return GetClient(config.DomainUrl, config.OAuth2Token, config.UserName, config.Password, config.ClientId, config.ClientSecret);
}
/// <summary>
/// Get OData HttpClient
/// </summary>
/// <param name="url"> </param>
/// <param name="username"> </param>
/// <param name="password"> </param>
/// <param name="authority"></param>
/// <param name="callerId"> </param>
/// <param name="clientId"> </param>
/// <param name="version"> </param>
/// <returns></returns>
public static HttpClient GetClient(string url, string redirectUrl
, string username, string password
, string clientId, string clientSecret
, string callerId = "", string version = "8.2", TimeSpan? timeSpan = null)
{
var webApiUrl = $"{url.TrimEnd('/')}/api/data/v{version}/";
var authHeader = GetAuthorization(webApiUrl, redirectUrl, clientId, clientSecret, username, password);
var client1 = new HttpClient()
{
BaseAddress = new Uri(webApiUrl),
Timeout = timeSpan ?? DEFAULT_TIMESPAN
};
var headers = client1.DefaultRequestHeaders;
headers.Authorization = authHeader;
headers.Add("OData-MaxVersion", "4.0");
headers.Add("OData-Version", "4.0");
headers.Add("Prefer", "odata.include-annotations=OData.Community.Display.V1.FormattedValue");
headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (!string.IsNullOrEmpty(callerId))
{
headers.Add("CallerObjectId", callerId);
headers.Add("MSCRMCallerID", callerId);
}
return client1;
}
/// <summary>
/// 分页列表数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="page"> 页码</param>
/// <param name="size"> 页阀</param>
/// <param name="entityName">实体名</param>
/// <param name="conditions">查询条件</param>
/// <param name="columns"> 查询字段</param>
/// <returns></returns>
public static IList<T> List<T>(this HttpClient client, int page, int size, string entityName, IList<ConditionItem> conditions, IList<string> columns, IList<OrderItem> orders = null) where T : TEntity
{
var fetchXml = FetchXmlExt.FetchXml(entityName, columns, conditions, orders, page, size);
var result = Activator.CreateInstance<T>();
return client.List<T>(result.SetName, fetchXml);
}
/// <summary>
/// 列表数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="entityName">实体名</param>
/// <param name="conditions">查询条件</param>
/// <param name="columns"> 查询字段</param>
/// <returns></returns>
public static IList<T> List<T>(this HttpClient client, string entityName, IList<ConditionItem> conditions, IList<string> columns, IList<OrderItem> orders = null) where T : TEntity
{
var fetchXml = FetchXmlExt.FetchXml(entityName, columns, conditions, orders);
var result = Activator.CreateInstance<T>();
return client.List<T>(result.SetName, fetchXml);
}
/// <summary>
/// 分页列表数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="page"> </param>
/// <param name="size"> </param>
/// <param name="entitySetName">实体集合名</param>
/// <param name="entityName"> 实体名</param>
/// <param name="conditions"> 查询条件</param>
/// <param name="columns"> 查询字段</param>
/// <returns></returns>
public static IList<T> List<T>(this HttpClient client, int page, int size, string entitySetName, string entityName, IList<ConditionItem> conditions, IList<string> columns, IList<OrderItem> orders = null) where T : TEntity
{
var fetchXml = FetchXmlExt.FetchXml(entityName, columns, conditions, orders, page, size);
return client.List<T>(entitySetName, fetchXml);
}
/// <summary>
/// 列表数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="entitySetName">实体集合名</param>
/// <param name="entityName"> 实体名</param>
/// <param name="conditions"> 查询条件</param>
/// <param name="columns"> 查询字段</param>
/// <returns></returns>
public static IList<T> List<T>(this HttpClient client, string entitySetName, string entityName, IList<ConditionItem> conditions, IList<string> columns, IList<OrderItem> orders = null) where T : TEntity
{
var fetchXml = FetchXmlExt.FetchXml(entityName, columns, conditions, orders);
return client.List<T>(entitySetName, fetchXml);
}
/// <summary>
/// 列表数据查询
/// </summary>
/// <typeparam name="T">Entity inherit by TEntity</typeparam>
/// <param name="client"> ODataExt.GetClient</param>
/// <param name="entitySetName">实体集合名</param>
/// <param name="fetchXml"> fetchXml</param>
/// <returns></returns>
public static IList<T> List<T>(this HttpClient client, string entitySetName, string fetchXml) where T : TEntity
{
var path = $"{entitySetName}?fetchXml={fetchXml}";
var entities = client.Get(path);
return entities.JsonToList<T>();
}
/// <summary>
/// Sends a PATCH request to update a resource.
/// </summary>
/// <param name="client"> </param>
/// <param name="uri"> </param>
/// <param name="body"> The payload to send to update the resource.</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
public static void Patch(this HttpClient client, Uri uri, object body, Dictionary<string, List<string>> headers = null)
{
client.PatchAsync(uri, body, headers).GetAwaiter().GetResult();
}
/// <summary>
/// Sends a PATCH request to update a resource.
/// </summary>
/// <param name="client"> </param>
/// <param name="url"> </param>
/// <param name="body"> </param>
/// <param name="headers"></param>
public static void Patch(this HttpClient client, string url, object body, Dictionary<string, List<string>> headers = null)
{
client.PatchAsync(url, body, headers).GetAwaiter().GetResult();
}
/// <summary>
/// Sends a PATCH request to update a resource asynchronously
/// </summary>
/// <param name="client"> </param>
/// <param name="url"> </param>
/// <param name="body"> </param>
/// <param name="headers"></param>
/// <returns></returns>
public static async Task PatchAsync(this HttpClient client, string url, object body, Dictionary<string, List<string>> headers = null)
{
var path = $"{client.BaseAddress.AbsoluteUri.TrimEnd('/')}/{url.Trim('/')}"; // client.BaseAddress
await client.PatchAsync(new Uri(path), body, headers);
}
/// <summary>
/// Sends a PATCH request to update a resource asynchronously
/// </summary>
/// <param name="client"> </param>
/// <param name="uri"> </param>
/// <param name="body"> The payload to send to update the resource.</param>
/// <param name="headers">Any custom headers to control optional behaviors.</param>
/// <returns>Task</returns>
public static async Task PatchAsync(this HttpClient client, Uri uri, object body, Dictionary<string, List<string>> headers = null)
{
try
{
using (var message = new HttpRequestMessage(new HttpMethod("PATCH"), uri))
{
message.Content = new StringContent(JObject.FromObject(body).ToString());
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
if (headers != null)
{
foreach (KeyValuePair<string, List<string>> header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
using (HttpResponseMessage response = await SendAsync(client, message))
response.Dispose();
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Posts a payload to the specified resource.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource</param>
/// <param name="body"> The payload to send.</param>
/// <param name="headers">Any headers to control optional behaviors.</param>
/// <returns>The response from the request.</returns>
public static JObject Post(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
return client.PostAsync(path, body, headers).GetAwaiter().GetResult(); ;
}
/// <summary>
/// Posts a payload to the specified resource.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource</param>
/// <param name="body"> The payload to send.</param>
/// <param name="headers">Any headers to control optional behaviors.</param>
/// <returns>The response from the request.</returns>
public static T Post<T>(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
return client.PostAsync(path, body, headers).GetAwaiter().GetResult().ToObject<T>();
}
/// <summary>
/// Posts a payload to the specified resource asynchronously.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the resource.</param>
/// <param name="body"> The payload to send.</param>
/// <param name="headers">Any headers to control optional behaviors.</param>
/// <returns>The response from the request.</returns>
public static async Task<JObject> PostAsync(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Post, path))
{
message.Content = new StringContent(JObject.FromObject(body).ToString());
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
if (headers != null)
{
foreach (KeyValuePair<string, List<string>> header in headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
using (HttpResponseMessage response = await SendAsync(client, message))
{
string content = await response.Content.ReadAsStringAsync();
if (string.IsNullOrEmpty(content))
{
return null;
}
return JObject.Parse(content);
}
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Creates an entity and returns the URI
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName">The entity set name of the entity to create.</param>
/// <param name="body"> The JObject containing the data of the entity to create.</param>
/// <returns>The Uri for the created entity record.</returns>
public static Uri PostCreate(this HttpClient client, string entitySetName, object body)
{
try
{
return client.PostCreateAsync(entitySetName, body).GetAwaiter().GetResult();
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Creates an entity asynchronously and returns the URI
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName">The entity set name of the entity to create.</param>
/// <param name="body"> The JObject containing the data of the entity to create.</param>
/// <returns>The Uri for the created entity record.</returns>
public static async Task<Uri> PostCreateAsync(this HttpClient client, string entitySetName, object body)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Post, entitySetName))
{
message.Content = new StringContent(JObject.FromObject(body).ToString());
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
using (HttpResponseMessage response = await SendAsync(client, message))
return new Uri(response.Headers.GetValues("OData-EntityId").FirstOrDefault());
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Sends a POST to create a typed entity and retrieves the created entity.
/// </summary>
/// <typeparam name="T">
/// The path to the entityset and any query string parameters to specify the properties to return.
/// </typeparam>
/// <param name="client"> </param>
/// <param name="path"> The path to the entityset.</param>
/// <param name="body"> The payload to send to create the entity record.</param>
/// <param name="headers">Any headers to control optional behaviors.</param>
/// <returns>The typed entity record created.</returns>
public static T PostGet<T>(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
return client.PostGetAsync<T>(path, body, headers).GetAwaiter().GetResult();
}
/// <summary>
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> </param>
/// <param name="body"> </param>
/// <param name="headers"></param>
/// <returns></returns>
public static JObject PostGet(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
return client.PostGetAsync(path, body, headers).GetAwaiter().GetResult();
}
/// <summary>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="client"> </param>
/// <param name="path"> </param>
/// <param name="body"> </param>
/// <param name="headers"></param>
/// <returns></returns>
public static async Task<T> PostGetAsync<T>(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
return (await client.PostGetAsync(path, body, headers)).ToObject<T>();
}
/// <summary>
/// Sents a POST to create an entity and retrieves the created entity asynchronously.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the entityset.</param>
/// <param name="body"> The payload to send to create the entity record.</param>
/// <param name="headers">Any headers to control optional behaviors.</param>
/// <returns>An object containing data for the created entity.</returns>
public static async Task<JObject> PostGetAsync(this HttpClient client, string path, object body, Dictionary<string, List<string>> headers = null)
{
if (headers == null)
{
headers = new Dictionary<string, List<string>>();
}
headers.Add("Prefer", new List<string> { "return=representation" });
return await client.PostAsync(path, body, headers);
}
/// <summary>
/// Updates a property of an entity
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the entity.</param>
/// <param name="property">The name of the property to update.</param>
/// <param name="value"> The value to set.</param>
public static void Put(this HttpClient client, string path, string property, dynamic value)
{
PutAsync(client, path, property, value).GetAwaiter().GetResult();
}
/// <summary>
/// Used to update metadata
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the metadata resource.</param>
/// <param name="metadataItem">The payload to send to update the metadata resource.</param>
/// <param name="mergeLabels"> Whether any existing language labels should be merged.</param>
public static void Put(this HttpClient client, string path, JObject metadataItem, bool mergeLabels)
{
client.PutAsync(path, metadataItem, mergeLabels).GetAwaiter().GetResult();
}
/// <summary>
/// Updates a property of an entity asychronously
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the entity.</param>
/// <param name="property">The name of the property to update.</param>
/// <param name="value"> The value to set.</param>
/// <returns>Task</returns>
public static async Task PutAsync(this HttpClient client, string path, string property, dynamic value)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Put, $"{path}/{property}"))
{
var body = new JObject
{
["value"] = value
};
message.Content = new StringContent(body.ToString());
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
using (HttpResponseMessage response = await SendAsync(client, message))
response.Dispose();
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Used to update metadata asychronously.
/// </summary>
/// <param name="client"> </param>
/// <param name="path"> The path to the metadata resource.</param>
/// <param name="metadataItem">The payload to send to update the metadata resource.</param>
/// <param name="mergeLabels"> Whether any existing language labels should be merged.</param>
public static async Task PutAsync(this HttpClient client, string path, JObject metadataItem, bool mergeLabels)
{
try
{
using (var message = new HttpRequestMessage(HttpMethod.Put, path))
{
if (mergeLabels)
{
message.Headers.Add("MSCRM.MergeLabels", "true");
message.Headers.Add("Consistency", "Strong");
}
message.Content = new StringContent(metadataItem.ToString());
message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
using (HttpResponseMessage response = await SendAsync(client, message))
response.Dispose();
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// 更新实体信息
/// </summary>
/// <param name="client"> </param>
/// <param name="entitySetName"></param>
/// <param name="entityId"> </param>
/// <param name="body"> </param>
public static void Update(this HttpClient client, string entitySetName, Guid entityId, object body)
{
client.Patch($"{entitySetName}({entityId})", body);
}
/// <summary>
/// 获取认证凭证
/// </summary>
/// <param name="webApiUrl"> </param>
/// <param name="redirectUrl"> </param>
/// <param name="clientId"> </param>
/// <param name="clientSecret"></param>
/// <param name="username"> </param>
/// <param name="password"> </param>
/// <returns></returns>
private static AuthenticationHeaderValue GetAuthorization(string webApiUrl, string redirectUrl, string clientId, string clientSecret, string username, string password)
{
var content = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
new KeyValuePair<string, string>("resource", webApiUrl),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password),
new KeyValuePair<string, string>("grant_type", "password")
};
var request = new HttpRequestMessage(HttpMethod.Post, redirectUrl) { Content = new FormUrlEncodedContent(content) };
var response = new HttpClient().Send(request);
var token = response.Content.ReadAsStringAsync().Result.ToModel<AdfsOAuth2Token>();
if (null == token || null == token.Access_token) throw new UnauthorizedAccessException();
var authHeader = new AuthenticationHeaderValue("Bearer", token.Access_token);
return authHeader;
}
/// <summary>
/// Parses the Web API error
/// </summary>
/// <param name="response">The response that failed.</param>
/// <returns></returns>
private static ServiceException ParseError(HttpResponseMessage response)
{
try
{
int code = 0;
string message = "no content returned",
content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (content.Length > 0)
{
var errorObject = JObject.Parse(content);
message = errorObject["error"]["message"].Value<string>();
try
{
code = Convert.ToInt32(errorObject["error"]["code"].Value<string>(), 16);
}
catch (Exception)
{
code = (int)response.StatusCode;
}
}
int statusCode = (int)response.StatusCode;
string reasonPhrase = response.ReasonPhrase;
return new ServiceException(code, statusCode, reasonPhrase, message);
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Sends all requests with retry capabilities
/// </summary>
/// <param name="client"> </param>
/// <param name="request"> The request to send</param>
/// <param name="httpCompletionOption">
/// Indicates if HttpClient operations should be considered completed either as soon as a
/// response is available, or after reading the entire response message including the content.
/// </param>
/// <param name="retryCount"> The number of retry attempts</param>
/// <returns>The response for the request.</returns>
private static async Task<HttpResponseMessage> SendAsync(HttpClient client,
HttpRequestMessage request,
HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseHeadersRead,
int retryCount = 0)
{
HttpResponseMessage response;
try
{
//The request is cloned so it can be sent again.
response = await client.SendAsync(request.Clone(), httpCompletionOption);
}
catch (Exception)
{
throw;
}
if (!response.IsSuccessStatusCode)
{
if ((int)response.StatusCode != 429)
{
//Not a service protection limit error
throw ParseError(response);
}
else
{
// Give up re-trying if exceeding the maxRetries
if (++retryCount >= 5)
{
throw ParseError(response);
}
int seconds;
//Try to use the Retry-After header value if it is returned.
if (response.Headers.Contains("Retry-After"))
{
seconds = int.Parse(response.Headers.GetValues("Retry-After").FirstOrDefault());
}
else
{
//Otherwise, use an exponential backoff strategy
seconds = (int)Math.Pow(2, retryCount);
}
Thread.Sleep(TimeSpan.FromSeconds(seconds));
return await SendAsync(client, request, httpCompletionOption, retryCount);
}
}
else
{
return response;
}
}
/// <summary>
/// An exception that captures data returned by the Web API
/// </summary>
public class ServiceException : Exception
{
/// <summary>
/// </summary>
/// <param name="errorcode"> </param>
/// <param name="statuscode"> </param>
/// <param name="reasonphrase"></param>
/// <param name="message"> </param>
public ServiceException(int errorcode, int statuscode, string reasonphrase, string message) : base(message)
{
ErrorCode = errorcode;
StatusCode = statuscode;
ReasonPhrase = reasonphrase;
}
/// <summary>
/// </summary>
public int ErrorCode { get; private set; }
/// <summary>
/// </summary>
public string ReasonPhrase { get; private set; }
/// <summary>
/// </summary>
public int StatusCode { get; private set; }
}
}
}