Dynamics Extentions OData

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; }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值