LoginSignup
3
2

More than 1 year has passed since last update.

Azure Functions 3.x で .NET 5.0 を使って Dataverse に格納されているレコードを取得する

Last updated at Posted at 2021-06-18

Power Apps と共に使われることが多く、また、 Dynamics 365 Customer Engagement 製品群の既定のデータストアとして機能するのが Dataverse です。.NET の技術を使って Dataverse の API を呼び出そうとすると、これまで長らく .NET Framework v4.x が必要でした。

先日、 NuGet パッケージ Microsoft.PowerPlatform.Dataverse.Client (執筆時点で Public preview) を使うと、 .NET 5.0 がサポートされていることを発見しました。

それを使ってみたくって、この記事では、これまた最近 .NET 5 がサポートされた Azure Functions アプリの中から利用してみます。

目的

以下のようなアプリを開発します。

  • Azure Functions アプリである
    • Azure Functions v3
    • .NET 5.0 の C# で開発する
    • Visual Studio Code を使う
    • 接続文字列をコードの中に記述せず、Azure ポータル上で登録する
    • Dataverse にアクセスして、エンティティ レコードの プライマリ画像 のデータを返す
      • NuGet パッケージ Microsoft.PowerPlatform.Dataverse.Client v0.4.12 (Public preview) を利用する

開発環境

  • .NET SDK 5.0.301
  • .NET Core SDK 3.1.410
    • (Azure Functions v3 で .NET 5 を採用する際にも .NET Core 3.1 が必要なようです。詳しく調べてません。)
  • Visual Studio Code v1.57.0

開発手順

① アプリの登録などの認証の設定

後に Azure Functions アプリ内から Dataverse にアクセスする際、 OAuth 認証が使用されます。そのために、以下のページの記述にしたがって、アプリを登録したり Dataverse へのアクセス権を付与する必要があります。「アプリ登録」セクションにおいては、アプリケーションの種類としては "Web アプリ /API" の方が該当します。

② Azure Functions アプリの開発

1. Azure ポータル上で Azure Functions の 関数アプリ を作成します

関数アプリ を新規作成します。以下のように設定しました。

  • ランタイムスタック: .NET
  • Version: 3.1 (他の選択肢がない)
2. Visual Studio Code 上での 関数アプリの関数の開発を開始します

以下のページを参考に開発の準備をします。

3. Visual Studio Code 内の Azure Functions 拡張機能を使ってプロジェクトを作成する

私は作成時に、以下の選択肢を選びました。

  • 言語: C# (これは、上記ページに記載の "C# コンパイル済み" に該当するはず)
  • .NET ランタイム: .NET 5
  • テンプレート: HTTP trigger
  • 関数名: HttpTrigger1
  • 名前空間: Keiji.Function
  • AccessRIghts (認証レベルのことであろう): Function
4. 接続文字列を登録する

関数アプリから Dataverse にアクセスするための、接続文字列を登録します。Azure ポータル上で当該 関数アプリ についての Configuration で、新しい Connection string を登録します。
ConnectionString.png
登録する Connection string として、私は名前を "MyCDSServer" とし、値は、以下のようなもので登録しました。「① アプリの登録などの認証の設定」で登録したアプリにしたがって、適宜ご自身の環境に合わせて編集ください。

MyCDSServer の値
AuthType=OAuth;Username=user@xxxxxxx.onmicrosoft.com;Password=12345;Url=https://xxxxxxx.crm.dynamics.com;AppId=xxxxxxxxx-8155-44e5-95e4-8615e5d12150;RedirectUri=https://localhost/appname;LoginPrompt=Auto

この辺りは、以下のページの記載を参考にしています。

5. 接続文字列をコードから取得する準備

Visual Studio Code にて、以下のページに記載の通り Startup.cs ファイルを追加します。namespace 関連以外は同じです。

Startup.cs
using System;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;

[assembly: FunctionsStartup(typeof(Keiji.Function.Startup))]

namespace Keiji.Function
{
    class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            string cs = Environment.GetEnvironmentVariable("ConnectionString");
            builder.ConfigurationBuilder.AddAzureAppConfiguration(cs);
        }

        public override void Configure(IFunctionsHostBuilder builder)
        {
        }
    }
}
6. 接続文字列をコードから取得する

Visual Studio Code で、関数である HttpTrigger1.cs で、実際に接続文字列を取得します。コンストラクタを追加するので、静的クラスではなく、動的クラスにするため、 static を取り除いています。(コードの全体は後程記載します。)

HttpTrigger1.cs の一部
    public class HttpTrigger1
    {
        private IConfiguration _configuration;
        public HttpTrigger1(IConfiguration configuration)
        {
            _configuration = configuration;
        }
7. 関数で受け取るためのクラスを作成する

後に Dataverse に対するクエリのために必要なクラスを作成します。具体的には、以下のような JSON 文字列を受け付けたいという設計です。
この例では、論理名 keiji_property というエンティティがあって、その中で プライマリ画像 データが格納されている属性の論理名が keiji_primaryimage である、という意味合いです。

{ "entityName": "keiji_property", "recordId": "76485703-2556-eb11-bb23-000d3a8b6647", "imageAttributeName": "keiji_primaryimage" }

その JSON 文字列に対応するクラスとして、今回は HttpTrigger1.cs の中に以下のようなクラスを作成しました。

HttpTrigger1.cs の一部
    public class MyClass
    {
        public string entityName { get; set; }
        public string recordId { get; set; }
        public string imageAttributeName { get; set; }
    }
8. NuGet パッケージのインストール

NuGet パッケージ Microsoft.PowerPlatform.Dataverse.Client をインストールします。

.NET CLI
dotnet add package Microsoft.PowerPlatform.Dataverse.Client --version 0.4.12
9. Dataverse へのクエリを発行して、レスポンスを返す

ServiceClient が NuGet パッケージ Microsoft.PowerPlatform.Dataverse.Client にて提供されていて、以下のようなコードで利用できます。今回は RetrieveAsync() を使って、単一の エンティティ レコード の 1個の属性の値を取得しようとしています。
ServiceClient を利用する際に、接続文字列を利用しています。

HttpTrigger1.cs の一部
            string connectionString = _configuration.GetConnectionString("MyCDSServer");
            var serviceClient = new ServiceClient(connectionString);

            var result = await serviceClient.RetrieveAsync(
                myClass.entityName,
                new Guid(myClass.recordId),
                new ColumnSet(myClass.imageAttributeName)
            );

            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Headers.Add("Content-Type", "text/plain; charset=utf-8");

            byte[] responseByteArray = (byte[])result[myClass.imageAttributeName];
            string imageBase64 = Convert.ToBase64String(responseByteArray, 0, responseByteArray.Length);
            response.WriteString("data:image/png;base64," + imageBase64);

            return response;
10. 関数のコード全体

以下が、関数のコード全体です。

HttpTrigger1.cs
using System;
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk.Query;

namespace Keiji.Function
{
    public class HttpTrigger1
    {
        private IConfiguration _configuration;
        public HttpTrigger1(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [Function("HttpTrigger1")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
            FunctionContext executionContext)
        {
            var logger = executionContext.GetLogger("HttpTrigger1");
            logger.LogInformation("C# HTTP trigger function processed a request.");

            byte[] byteArray = new byte[req.Body.Length];
            await req.Body.ReadAsync(byteArray, 0, (int)req.Body.Length);
            string jsonContent = System.Text.Encoding.UTF8.GetString(byteArray);
            MyClass myClass = JsonSerializer.Deserialize<MyClass>(jsonContent);

            string connectionString = _configuration.GetConnectionString("MyCDSServer");
            var serviceClient = new ServiceClient(connectionString);

            var result = await serviceClient.RetrieveAsync(
                myClass.entityName,
                new Guid(myClass.recordId),
                new ColumnSet(myClass.imageAttributeName)
            );

            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Headers.Add("Content-Type", "text/plain; charset=utf-8");

            byte[] responseByteArray = (byte[])result[myClass.imageAttributeName];
            string imageBase64 = Convert.ToBase64String(responseByteArray, 0, responseByteArray.Length);
            response.WriteString("data:image/png;base64," + imageBase64);

            return response;
        }
    }

    public class MyClass
    {
        public string entityName { get; set; }
        public string recordId { get; set; }
        public string imageAttributeName { get; set; }
    }
}
11. (参考) .csproj ファイル

参考までに、 .csproj ファイルを記載します。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.12" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.0.3" OutputItemType="Analyzer" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.AzureAppConfiguration" Version="4.4.0" />
    <PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="0.4.12" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

③ Azure Functions アプリのデプロイ

Visual Studio Code 内の Azure Functions 拡張機能を使って、Azure 上の関数アプリにデプロイします。

④ 動作確認

1. 関数を呼び出すための URL を取得

Visual Studio Code 内の Azure Functions 拡張機能を使って、関数の URL を取得 ("Copy Function Url") します。
CopyFunctionUrl.png

その URL は以下のようなものになります。

https://keiji-function-app-xxxxxx.azurewebsites.net/api/httptrigger1?code=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2. Postman を使って動作確認

Postman を使って、先ほど得た URL に対して、 POST メソッドを使って、先に紹介した JSON 文字列を Body に入力して Send します。
以下のように画像データを取得できると成功です。
なお、(今回の Dataverse へのクエリの例では) 事前に Dataverse の当該エンティティで当該レコードIDを持つレコードに、画像が登録されていることを確認してください。
Postman.png

リファレンス

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2