次の方法で共有


Retail Server 拡張 API の作成 (Retail SDK バージョン 10.0.11 以降)

ノート

Dynamics 365 Commerce の小売関連グループは、Yammer から Viva Engage に移行しました。 新しいViva Engage コミュニティにアクセスできない場合は、追加するフォーム (https://aka.ms/JoinD365commerceVivaEngageCommunity) に入力し、最新のディスカッションに参加してください。

この記事では、新しい Retail Server アプリケーション プログラミング インターフェイス (API) の作成方法、および 販売時点管理 (POS) または他のクライアントがそれを使用できるようにするための公開方法について説明します。 既存の Retail Server API の変更はサポートされていません。

この記事は、Retail ソフトウェア開発キット (SDK) バージョン 10.0.11 以降に適用されます。

Retail SDK には、Commerce Runtime (CRT) を含む、エンドツーエンドの Retail Server 拡張機能のサンプルがいくつか用意されているのみです。 これらのサンプルをテンプレートとして使用して、拡張機能を開始します。 サンプル拡張機能は、RetailSDK\SampleExtensions\RetailServer フォルダーで見つけることができます。

ノート

大量を処理し、チャネル データベースから読み取る必要がある新しい Retail Server 拡張機能 API を作成する場合は、データベース読み取りのキャッシュを有効にします。 そうしないと、拡張機能で消費される Retail Server およびチャネル データベース リソースが多すぎるため、コマース スケール ユニット (CSU) 全体のパフォーマンスの問題が発生し、ビジネスに影響を与える可能性があります。

Retail SDK のエンドツーエンド サンプル リポジトリ

サンプル拡張機能
(RetailSDK\SampleExtensions\RetailServer)
CRT サンプル
(RetailSDK\SampleExtensions\CommerceRuntime)
POS サンプル
(RetailSDK\POS\Extensions)
Extensions.ストア営業時間サンプル Extensions.ストア営業時間サンプル StoreHoursSample
Extensions.SalesTransactionSignatureSample Extensions.SalesTransactionSignatureSample 販売取引署名サンプル
拡張機能.納品書サンプル印刷 拡張機能.納品書サンプル印刷
Extensions.CrossLoyaltySample Extensions.CrossLoyaltySample

拡張機能クラス ダイアグラム

次の図は、拡張機能のクラス ダイアグラムを示しています。

Commerce Scale Unit 拡張クラスの図。

ノート

リテール サーバーでは、 IController 拡張機能と CommerceController 拡張機能の両方の読み込みがサポートされていません。 両方の種類の拡張機能を含める場合、Retail サーバーの読み込みに失敗します。 拡張機能には、 IController または CommerceControllerが必要です。 IController拡張機能に移行する場合は、すべての Retail サーバー拡張機能をIControllerに移行します。

新しい Retail Server API の作成

  1. CRT 拡張機能を作成します。 Retail Server 拡張機能を作成する前に、CRT 拡張機能を作成します。 Retail Server API には、パラメーターで CRT を呼び出すロジック以外のロジックはありません。

  2. Microsoft .NET Framework バージョン .NET Standard 2.0 をターゲット フレームワークとして使用する新しい C# クラス ライブラリ プロジェクトを作成します。 または、Retail ソフトウェア開発キット (SDK) に含まれている Retail Server のサンプルのいずれかをテンプレートとして使用できます。

  3. Retail Server 拡張機能プロジェクトで、CRT 拡張機能ライブラリまたはプロジェクトへの参照を追加します。 この参照を使用して、CRT 要求、応答およびエンティティを呼び出すことができます。

  4. Retail Server 拡張機能プロジェクトで、NuGet パッケージマネージャーを使用して Microsoft.Dynamics.Commerce.Runtime.Hosting.Contracts パッケージを追加します。 NuGet パッケージは、RetailSDK\pkgs フォルダにあります。

  5. 新しい公開コントローラー クラスを作成し、IController からクラスを拡張します。 このコントローラー クラスには、Retail Server API が公開するメソッドが含まれています。 コントローラー クラスはパブリックである必要があります。

  6. コントローラー クラス内で、CRT 要求を呼び出すメソッドを追加します。 CustomerControllerSalesOrdersControllerProductController などの既存のコントローラー クラスから新しいコントローラー クラスを拡張しないでください。 拡張クラスでは、IController クラスのみを拡張する必要があります。

  7. コントローラー クラス (コントローラー クラス名) 上で RoutePrefix 属性を追加します。

    [RoutePrefix("SimpleExtension")]
    

    ノート

    RoutePrefix 属性の追加は省略可能です。 RoutePrefix 属性を追加する場合、カスタム エンティティのみを含む BindEntity 属性を追加する必要があります。 標準のRoutePrefix(例えば、顧客や製品)の追加や標準のエンティティ(製品、カート、顧客など)のバインドはサポートされていません。 RoutePrefix および顧客カスタム エンティティのみを追加することができます。

  8. コントローラー クラスで BindEntity 属性を追加します。 この手順はオプションです。 RoutePrefix を追加し、カスタム エンティティを返す場合のみ属性を追加します。

    [BindEntity(typeof(SimpleEntity))]
    

ノート

拡張クラスがエンティティにバインドされている場合は、手順 7 と 8 が必要です。 これらの手順は、エンティティではなく、単純型を返す無制限のコントローラー クラスには必要ありません。

次のサンプル コードでは、エンティティ、文字列、およびブール値を返す単純な Retail Server API を作成します。 サンプルで使用される CRT 要求と応答は、このサンプルには含まれていません。 CRT 要求および応答の例については、Commerce Runtime (CRT) の拡張機能およびトリガーを参照してください。

カスタム エンティティにバインドされるコントローラ クラスのサンプルコード

ノート

拡張コードは、Customer、Cart、Product などの既存のエンティティにバインドしないでください。 API がコレクションを返す場合は、 IEnumerable<T>型のみを返す必要があります。 Dictionary <String, String>など、他の型の返しはサポートされていません。 その結果、System.Collections.Generic.Dictionary2[System.String,System.String] is not supported. などのエラーが発生する可能性があります。コレクションを返すために、Commerce API は PageResult<T> を使用し、IEnumerable<T> を実装しています。 このパターンに従い、コレクションを返します。

// New extended controller.
[RoutePrefix("SimpleExtension")]
[BindEntity(typeof(SimpleEntity))]
public class SimpleExtensionController : IController
{
    /// <summary>
    /// The action to get the string value.
    /// </summary>
    /// <param name="context">The context parameters.</param>
    /// <param name="stringValue">The string value parameters.</param>
    /// <returns>The string value.</returns>
    [HttpPost]
    [Authorization(CommerceRoles.Customer, CommerceRoles.Employee)]
    public async Task<string> GetStringValue(IEndpointContext context, string stringValue)
    {
        GetStringValueResponse resp = await context.ExecuteAsync<GetStringValueResponse>
                                    (new GetStringValueRequest(stringValue)).ConfigureAwait(false);
        return resp.StringValue;
    }

    /// <summary>
    /// The action to get the bool value.
    /// </summary>
    /// <param name="context">The context parameters.</param>
    /// <param name="boolValue">The string value parameters.</param>
    /// <returns>The bool value.</returns>
    [HttpPost]
    [Authorization(CommerceRoles.Customer, CommerceRoles.Employee)]
    public async Task<bool> GetBoolValue(IEndpointContext context, string boolValue)
    {
        GetBoolValueResponse resp = await context.ExecuteAsync<GetBoolValueResponse>
                                    (new GetBoolValueRequest(boolValue)).ConfigureAwait(false);
        return resp.BoolValue;
    }

    /// <summary>
    /// The action to get the simple entity.
    /// </summary>
    /// <param name="context">The context parameters.</param>
    /// <param name="name">The name parameters.</param>
    /// <returns>The simple entity.</returns>
    [HttpPost]
    [Authorization(CommerceRoles.Customer, CommerceRoles.Employee)]
    public async Task<SimpleEntity> GetSimpleEntity(IEndpointContext context, string name)
    {
        GetSimpleEntityResponse resp = await context.ExecuteAsync<GetSimpleEntityResponse>
                                        (new GetSimpleEntityRequest(name)).ConfigureAwait(false);
        return resp.SimpleEntityObj;
    }
}

カスタム エンティティにバインドされていないコントローラー クラスのサンプル コード

namespace Contoso.UnboundController.Sample
{
    using System.Threading.Tasks;
    using Microsoft.Dynamics.Commerce.Runtime.DataModel;
    using Microsoft.Dynamics.Commerce.Runtime.Hosting.Contracts;

    /// <summary>
    /// An extension unbounded controller sample.
    /// </summary>
    public class UnboundController : IController
    {
        /// <summary>
        /// A simple GET endpoint to demonstrate GET endpoints on an unbound controller.
        /// </summary>
        /// <returns>A simple true value to indicate the endpoint was reached.</returns>
        [HttpGet]
        [Authorization(CommerceRoles.Anonymous, CommerceRoles.Application, CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee, CommerceRoles.Storefront)]
        public Task<bool> SampleGet()
        {
            return Task.FromResult(true);
        }

        /// <summary>
        /// A simple POST endpoint to demonstrate POST endpoints on an unbound controller.
        /// </summary>
        /// <returns>A simple true value to indicate the endpoint was reached.</returns>
        [HttpPost]
        [Authorization(CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee)]
        public Task<bool> SamplePost()
        {
            return Task.FromResult(true);
        }
    }
}

Retail Server API では、さまざまな承認ロールがサポートされています。 コントローラー メソッドへのアクセスは、コントローラー メソッドの Authorizations 属性で指定した承認ロールに基づいて許可されます。 次の例は、サポートされている承認ロールを示しています。 拡張機能コードでは、Authorizations 属性の代わりに CommerceAuthorization 属性を使用しないでください。 CommerceAuthorization 属性は、10.0.11 より前の SDK バージョンでのみサポートされています。

// Represents the type of logon type.
[DataContract]
public static class CommerceRoles
{
    // Anonymous Role.
    [DataMember]
    public const string Anonymous = "Anonymous";

    // SharePoint Role used by Connector.
    [DataMember]
    public const string Storefront = "Storefront";

    // Employee Role.
    [DataMember]
    public const string Employee = "Employee";

    // Customer Role.
    [DataMember]
    public const string Customer = "Customer";

    // Represents the Device level of authentication.
    [DataMember]
    public const string Device = "Device";

    // Represents Application level of authentication.
    [DataMember]
    public const string Application = "Application";

    // The list of all possible Microsoft.Dynamics.Commerce.Runtime.DataModel.CommerceRoles values.
    public static readonly string[] All;
}

Retail Server API のページングのサポート

リリース 10.0.19 以降、API でページングが必要な場合は、 QueryResultSettings パラメーターを API に追加し、クライアントから値を渡します。 QueryResultSettings には、PagingInfo およびレコードがフェッチまたはスキップするための他のパラメーターが含まれます。

拡張機能は、CRT 要求に QueryResultSettings を渡すことができます。 CRT 要求は、データベース クエリがある場合に使用できます。

完全なサンプルコードは、Retail SDK に含まれています: RetailSDK\SampleExtensions\CommerceRuntime\Extensions.StoreHoursSample\StoreHoursDataService.cs、RetailSDK\SampleExtensions\RetailServer\Extensions.StoreHoursSample\StoreHoursController.cs


    [HttpPost]
        [Authorization(CommerceRoles.Anonymous, CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee)]
        public async Task<PagedResult<SampleDataModel.StoreDayHours>> GetStoreDaysByStore(IEndpointContext context, string StoreNumber, QueryResultSettings queryResultSettings)
        {
            var request = new GetStoreHoursDataRequest(StoreNumber) { QueryResultSettings = queryResultSettings };
            var hoursResponse = await context.ExecuteAsync<GetStoreHoursDataResponse>(request).ConfigureAwait(false);
            return hoursResponse.DayHours;
        }


private async Task<CommerceEntity> GetStoreDayHoursAsync(GetStoreHoursDataRequest request)
            {
                ThrowIf.Null(request, "request");

                using (DatabaseContext databaseContext = new DatabaseContext(request.RequestContext))
                {
                    var query = new SqlPagedQuery(request.QueryResultSettings)
                    {
                        DatabaseSchema = "ext",
                        Select = new ColumnSet("DAY", "OPENTIME", "CLOSINGTIME", "RECID"),
                        From = "CONTOSORETAILSTOREHOURSVIEW",
                        Where = "STORENUMBER = @storeNumber",
                    };

                    query.Parameters["@storeNumber"] = request.StoreNumber;
                    return new GetStoreHoursDataResponse(await databaseContext.ReadEntityAsync<DataModel.StoreDayHours>(query).ConfigureAwait(false));
                }
            }

拡張機能の登録

  1. 拡張機能プロジェクトをビルドし、バイナリを \RetailServer\webroot\bin\Ext フォルダーにコピーします。

  2. extensionComposition セクションで新しい拡張ライブラリ名を追加して、\RetailServer\webroot フォルダーの Commerce Scale Unit web.config ファイルを更新します。

    <extensionComposition>
        <!-- Use fully qualified assembly names for ALL if you need to support loading from the Global Assembly Cache.
        If you host in an application with a bin folder, this is not required. -->
        <add source="assembly" value="SimpleExtensionSample" >
    </extensionComposition>
    
  3. Internet Information Services (IIS) で、Commerce Scale Unit を再起動して新しい拡張機能を読み込みます。

拡張機能の検証

  1. 拡張機能が正常に読み込まれたことを確認するには、Retail Server メタデータを参照します。 エンティティおよびメソッドが一覧に表示されることを確認します。 メタデータを参照するには、Web ブラウザーの次の形式で URL を開きます。

    https://RS-URL/Commerce/$metadata

  2. クライアントで Retail Server 拡張機能を呼び出すには、クライアント TypeScript プロキシを生成します。 プロキシを使用して、クライアントから新しい Retail Server API を呼び出します。

Retail Server 拡張 API を使用して、EdmModelExtender ファイルを拡張機能に追加する必要はありません。 これらのファイルは、Retail SDK バージョン 10.0.10 以前を使用している場合にのみ必要です。

RS 拡張機能のデバッグ

Visual Studioで RS 拡張プロジェクトをデバッグするには、Debug > プロセスへのアタッチに移動します。 w3wp.exe (Retail Server の IIS プロセス) を選択します。 複数の w3wp.exe プロセスがある場合は、プロセス ID に基づいて適切なプロセスを使用します。 リテール サーバー プロセス ID は、 IIS > Worker プロセス を使用するか、コマンド プロンプトとタスク リスト コマンドを使用して確認できます。

POS の TypeScript プロキシを生成する

POS は TypeScript プロキシを使用して、Retail Server API と CRT エンティティにアクセスします。 プロキシ クラスは、マネージャー クラスまたはラッパーとして機能し、プロキシ拡張機能が小売サーバー API やエンティティ メタデータを手動で検索することなく、小売サーバー API にアクセスします。

プロキシ ファイルを生成する手順

  1. Visual Studio 2017 では、\RetailSDK\Code\SampleExtensions\TypeScriptProxy\TypeScriptProxy.Extensions.StoreHoursSample\Proxies.TypeScriptProxy.Extensions.StoreHoursSample.csproj からサンプル プロキシ テンプレート プロジェクトを開きます。 別の名前を使用する場合は、プロジェクトの名前を変更します。

  2. このプロキシ テンプレート プロジェクトに Retail Server 拡張機能プロジェクトをプロジェクト参照として追加します。 既存の StoreHoursSample プロジェクト参照を削除します。

  3. Proxies.TypeScriptProxy.Extensions.StoreHoursSample.csproj プロジェクトを右クリックし、Edit Proxies.TypeScriptProxy.Extensions.StoreHoursSample.csproj を選択します。

  4. <RetailServerExtensionAssemblies> ノードの下に、Retail Server 拡張機能のアセンブリ名を指定します。 次の例は、アセンブリ名を追加する方法を示しています。

    <ItemGroup>
        <RetailServerExtensionAssemblies Include="..\..\RetailServer\Extensions.Sample\bin\$(Configuration)\net461\$(AssemblyNamePrefix).RetailServer.Extension.Sample.dll" />
    </ItemGroup>
    
  5. <Copy> ノードで、生成されたプロキシ ファイルが POS 拡張フォルダーに自動的にコピーされるように、POS 拡張フォルダーの DestinationFolder パスを更新します。 生成されたプロキシ ファイルは 、\RetailSDK\Code\SampleExtensions\TypeScriptProxy\TypeScriptProxy.Extensions.StoreHoursSample\DataService にもコピーされます。 次の例は、パスを更新する方法を示しています。

    <Copy SourceFiles="@(GeneratedDataServiceContracts)" DestinationFolder="$(SdkRootPath)\POS\Extensions\Sample\DataService" SkipUnchangedFiles="true" />
    
  6. 変更を加えた後、プロキシ プロジェクトをビルドして TypeScript プロキシ ファイルを生成します。 ビルドが完了すると、プロキシ ファイルは \RetailSDK\Code\SampleExtensions\TypeScriptProxy\TypeScriptProxy.Extensions.StoreHoursSample\DataService フォルダーと 、コピー コマンドで指定したフォルダーで使用できます。 パスとフォルダー パスは、フォルダ構造によって異なる場合があります。

オフラインの Retail Server 拡張機能

Retail Server 拡張機能は、Microsoft.Dynamics.Commerce.Runtime.Hosting.Contracts API を使用してオフライン実装でビルドできます。 個別の C# プロキシ ライブラリを生成する必要はありません。 Retail Server 拡張機能ライブラリを \Microsoft Dynamics 365\70\Retail Modern POS\ClientBroker\ext フォルダーにコピーし、RetailProxy.MPOSOffline.ext 構成ファイルを更新して、このライブラリを含めます。 この拡張機能では、TypeScript プロキシのみを生成する必要があります。 SDK サンプルは、\RetailSDK\SampleExtensions\TypeScriptProxy) フォルダーにあります。

次の例は、RetailProxy.MPOSOffline.ext 構成ファイル内の要素の追加を更新する方法を示しています。

<?xml version="1.0" encoding="utf-8"?>
<retailProxyExtensions>
    <composition>
        <add source="assembly" value="Contoso.RetailServer.StoreHoursSample" />
    </composition>
</retailProxyExtensions>