目次
Dataverse の WebAPI を C# で叩く系記事たちの目次です。
今回の本文
前回の続きです。
前回までで、「はい/いいえ」列の追加ができていませんでした。
■ はい/いいえ
はい/いいえ は boolean 型のようですが、表現としてのラベル 「はい」「いいえ」がある選択肢のような感じになっています。
つまり、選択肢定義を追加してやらないとエラーになります。
■ 動かしかた
まずいったん別途 API で列を追加するのとは別のテーブルの定義 JSON を取得してファイルに保存します。
コマンドライン引数で、列が追加される方のテーブルの URL と、先に取得した JSON のファイルパスを指定します。
URL Web の GUI で 「テーブル 定義への API リンク」をした時の URL です。
■ 概要
- テーブルの列定義の JSON の value を取り出してループ
- その定義から
MetadataId、CreatedOn、ModifiedOnを削除 - Post。
簡単ですね。
■ コード
クラス二つになりました。
using DataverseClientShared; using System.Text; if (args.Length < 2) { Console.WriteLine("Usage: DataverseAddAttributes <EntityDefinition URL> <Path to JSON file>"); Console.WriteLine("Example"); Console.WriteLine("<EntityDefinition URL> : https://orgxxxxxxxx.crmx.dynamics.com/api/data/v9.2/EntityDefinitions(LogicalName='xxxxx_xxxxxxxx')"); Console.WriteLine("<Path to JSON file> : C:\example\example.json"); return; } string s = args[0] + "/Attributes"; string d = args[1]; ; HttpHelper http = (await HttpHelper.Instance.Init(s, new HttpClient())).Item1; Newtonsoft.Json.Linq.JObject originJson = (Newtonsoft.Json.Linq.JObject)Newtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(d))!; var value = originJson.SelectToken("value"); if (value != null) foreach (var attribute in value) { attribute.SelectToken("MetadataId")?.Parent?.Remove(); attribute.SelectToken("CreatedOn")?.Parent?.Remove(); attribute.SelectToken("ModifiedOn")?.Parent?.Remove(); var type = ((Newtonsoft.Json.Linq.JValue?)attribute["@odata.type"])?.Value?.ToString(); if (type == "#Microsoft.Dynamics.CRM.PicklistAttributeMetadata" || type == "#Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata") if (attribute.SelectToken("OptionSet") == null || attribute.SelectToken("OptionSet")?.SelectToken("Options") == null || attribute.SelectToken("OptionSet")?.SelectToken("Options")?.Any() == false) { attribute["OptionSet"] = new Newtonsoft.Json.Linq.JObject { ["Options"] = new Newtonsoft.Json.Linq.JArray { new Newtonsoft.Json.Linq.JObject { ["Value"] = 1, ["Label"] = new Newtonsoft.Json.Linq.JObject { ["LocalizedLabels"] = new Newtonsoft.Json.Linq.JArray { new Newtonsoft.Json.Linq.JObject { ["Label"] = "dummy001", ["LanguageCode"] = 1041 } } } } }, ["IsGlobal"] = false, ["OptionSetType"] = "Picklist" }; } if (type == "#Microsoft.Dynamics.CRM.BooleanAttributeMetadata") if (attribute.SelectToken("OptionSet") == null || attribute.SelectToken("OptionSet")?.SelectToken("Options") == null || attribute.SelectToken("OptionSet")?.SelectToken("Options")?.Any() == false) { attribute["OptionSet"] = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>( """ { "TrueOption": { "Value": 1, "Label": { "@odata.type": "Microsoft.Dynamics.CRM.Label", "LocalizedLabels": [ { "@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel", "Label": "はい", "LanguageCode": 1041, "IsManaged": false } ] } }, "FalseOption": { "Value": 0, "Label": { "@odata.type": "Microsoft.Dynamics.CRM.Label", "LocalizedLabels": [ { "@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel", "Label": "いいえ", "LanguageCode": 1041, "IsManaged": false } ] } }, "OptionSetType": "Boolean" } """ ); } var json = Newtonsoft.Json.JsonConvert.SerializeObject(attribute); Console.WriteLine(await PostAsync(http, json)); } async Task<string> PostAsync(HttpHelper http, string json) { string? jsonContent = ""; var content = new StringContent(json, Encoding.UTF8, "application/json"); if (http.HttpClient == null) return jsonContent; var response = await http.HttpClient.PostAsync(http.Path, content); if (response.IsSuccessStatusCode) { Console.WriteLine($"Success: {response.StatusCode}"); jsonContent = await response.Content.ReadAsStringAsync(); } else { Console.WriteLine($"Error: {response.StatusCode}"); jsonContent = await response.Content.ReadAsStringAsync(); Console.WriteLine(jsonContent); } return jsonContent ?? ""; }
次のクラスはこれまで毎回書いてきた定型的な部分をクラスでまとめたものです。
namespace DataverseClientShared; public class HttpHelper { public static HttpHelper Instance { get; } = new HttpHelper(); public string? Resource { get; private set; } public Uri? BaseAddress { get; private set; } public string? Path { get; private set; } public static string ClientId { get; } = "51f81489-12ee-4a9e-aaae-a2591f45987d"; public static string RedirectUri { get; } = "http://localhost"; private HttpHelper() { } public Microsoft.Identity.Client.AuthenticationResult? Token { get; private set; } public HttpClient? HttpClient { get; private set; } public HttpHelper InitPath(string fullPath) { var uri = new Uri(fullPath); Resource = uri.Scheme + "://" + uri.Host; BaseAddress = new Uri(Instance.Resource + string.Join("", uri.Segments.Take(4))); Path = new string(uri.PathAndQuery.Skip(BaseAddress.AbsolutePath.Length).ToArray()); return Instance; } public async Task<Microsoft.Identity.Client.AuthenticationResult> AuthenticateAsync() { var authBuilder = Microsoft.Identity.Client.PublicClientApplicationBuilder.Create(ClientId) .WithAuthority(Microsoft.Identity.Client.AadAuthorityAudience.AzureAdMultipleOrgs) .WithRedirectUri(RedirectUri) .Build(); string[] scopes = { Resource + "/user_impersonation" }; Microsoft.Identity.Client.AuthenticationResult token = await authBuilder.AcquireTokenInteractive(scopes).ExecuteAsync(); return Token = token; } public HttpClient SetHttpClient(HttpClient httpClient) { var client = HttpClient = httpClient ?? HttpClient ?? new HttpClient(); client.BaseAddress = BaseAddress; client.Timeout = new TimeSpan(0, 2, 0); System.Net.Http.Headers.HttpRequestHeaders headers = client.DefaultRequestHeaders; headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Token?.AccessToken); headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); return client; } public async Task<(HttpHelper, HttpClient)> Init(string fullPath, HttpClient httpClient) { InitPath(fullPath); await AuthenticateAsync(); SetHttpClient(httpClient); return (Instance!, HttpClient!); } }
■ GitHub
GitHub リポジトリにコードを置いて試しています。