以下の内容はhttps://rksoftware.hatenablog.com/entry/2025/08/28/190710より取得しました。


Dataverse の WebAPI で Dataverse のテーブルに列追加する C# コード その5

目次

Dataverse の WebAPI を C# で叩く系記事たちの目次です。

rksoftware.hatenablog.com

今回の本文

rksoftware.hatenablog.com

前回の続きです。
前回までで、「はい/いいえ」列の追加ができていませんでした。

■ はい/いいえ

はい/いいえ は boolean 型のようですが、表現としてのラベル 「はい」「いいえ」がある選択肢のような感じになっています。
つまり、選択肢定義を追加してやらないとエラーになります。

■ 動かしかた

まずいったん別途 API で列を追加するのとは別のテーブルの定義 JSON を取得してファイルに保存します。
コマンドライン引数で、列が追加される方のテーブルの URL と、先に取得した JSON のファイルパスを指定します。

URL Web の GUI で 「テーブル 定義への API リンク」をした時の URL です。

■ 概要

  1. テーブルの列定義の JSON の value を取り出してループ
  2. その定義から MetadataIdCreatedOnModifiedOn を削除
  3. 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 リポジトリにコードを置いて試しています。

github.com




以上の内容はhttps://rksoftware.hatenablog.com/entry/2025/08/28/190710より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14