ストレージに直接書き込む

この記事の対象: SDK v4

ミドルウェアまたはコンテキスト オブジェクトを使用せずに、ストレージ オブジェクトに対して直接読み取りや書き込みを行うことができます。 ボットが会話の保持に使用するデータ、またはボットの会話フロー外にあるソースのデータについては、この方法が適切な可能性があります。 このデータ ストレージ モデルでは、データは、状態マネージャーを使用せずに、ストレージから直接データを読み取られます。 この記事のコード例では、メモリCosmos DBAzure Blob、および Azure Blob Transcript ストレージを使用してストレージに対するデータの読み取りや書き込みを行う方法を示します。

Note

Bot Framework JavaScript SDK、C#、Python SDK は引き続きサポートされますが、Java SDK については、最終的な長期サポートは 2023 年 11 月に終了する予定です。 このリポジトリ内の重要なセキュリティとバグの修正のみが行われます。

Java SDK を使用して構築された既存のボットは引き続き機能します。

新しいボットの構築については、Power Virtual Agents の使用を検討し、適切なチャットボット ソリューションの選択についてお読みください。

詳細については、「The future of bot building」をご覧ください。

前提条件

  • Azure サブスクリプションをお持ちでない場合は、開始する前に無料アカウントを作成してください。
  • ローカルでのボットの作成に関する知識。
  • Visual Studio (C# )Node.js、または Yeoman 用の Bot Framework SDK v4 テンプレート。

Note

Visual Studio からテンプレートをインストールできます。

  1. メニューで、[拡張機能][拡張機能の管理] の順番に選択します。
  2. [拡張機能の管理] ダイアログで、Visual Studio 用 Bot Framework v4 SDK テンプレートを検索してインストールします。

.NET ボットを Azure にデプロイする方法については、ボットをプロビジョニングして発行する方法を参照してください。

このサンプルについて

この記事のサンプル コードは、基本的なエコー ボットの構造で始まり、コードを追加することでそのボットの機能を拡張します (下記を参照)。 拡張されたこのコードにより、受信したユーザー入力が保持されるリストが作成されます。 ターンごとに、メモリにセーブされたユーザー入力の完全なリストがユーザーにエコーバックされます。 この入力リストを含むデータ構造はその後変更され、ストレージに保存されます。 このサンプル コードには追加機能が追加されているため、さまざまな種類のストレージについて説明します。

メモリ ストレージ

Bot Framework SDK を使用すると、メモリ内ストレージを使用してユーザー入力を格納することができます。 メモリ内ストレージはボットが再起動されるたびにクリアされるため、テスト目的に最適であり、運用環境での使用を目的としたものではありません。 データベース ストレージなどの永続的なストレージ類は運用環境のボットに最適です。

基本のボットを作成する

このトピックの残りの部分では、エコー ボットについては取り上げません。 エコー ボットのサンプル コードは、ボットの作成クイックスタートの手順に従ってローカルに構築できます。

EchoBot.cs のコードを次のコードに置き換えます。

using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

// Represents a bot saves and echoes back user input.
public class EchoBot : ActivityHandler
{
   // Create local Memory Storage.
   private static readonly MemoryStorage _myStorage = new MemoryStorage();

   // Create cancellation token (used by Async Write operation).
   public CancellationToken cancellationToken { get; private set; }

   // Class for storing a log of utterances (text of messages) as a list.
   public class UtteranceLog : IStoreItem
   {
      // A list of things that users have said to the bot
      public List<string> UtteranceList { get; } = new List<string>();

      // The number of conversational turns that have occurred
      public int TurnNumber { get; set; } = 0;

      // Create concurrency control where this is used.
      public string ETag { get; set; } = "*";
   }

   // Echo back user input.
   protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
   {
      // preserve user input.
      var utterance = turnContext.Activity.Text;

      // Make empty local log-items list.
      UtteranceLog logItems = null;

      // See if there are previous messages saved in storage.
      try
      {
         string[] utteranceList = { "UtteranceLog" };
         logItems = _myStorage.ReadAsync<UtteranceLog>(utteranceList).Result?.FirstOrDefault().Value;
      }
      catch
      {
         // Inform the user an error occurred.
         await turnContext.SendActivityAsync("Sorry, something went wrong reading your stored messages!");
      }

      // If no stored messages were found, create and store a new entry.
      if (logItems is null)
      {
         // Add the current utterance to a new object.
         logItems = new UtteranceLog();
         logItems.UtteranceList.Add(utterance);

         // Set initial turn counter to 1.
         logItems.TurnNumber++;

         // Show user new user message.
         await turnContext.SendActivityAsync($"{logItems.TurnNumber}: The list is now: {string.Join(", ", logItems.UtteranceList)}");

         // Create dictionary object to hold received user messages.
         var changes = new Dictionary<string, object>();
         {
            changes.Add("UtteranceLog", logItems);
         }
         try
         {
            // Save the user message to your Storage.
            await _myStorage.WriteAsync(changes, cancellationToken);
         }
         catch
         {
            // Inform the user an error occurred.
            await turnContext.SendActivityAsync("Sorry, something went wrong storing your message!");
         }
      }
      // Else, our storage already contained saved user messages, add new one to the list.
      else
      {
         // add new message to list of messages to display.
         logItems.UtteranceList.Add(utterance);
         // increment turn counter.
         logItems.TurnNumber++;

         // show user new list of saved messages.
         await turnContext.SendActivityAsync($"{logItems.TurnNumber}: The list is now: {string.Join(", ", logItems.UtteranceList)}");

         // Create Dictionary object to hold new list of messages.
         var changes = new Dictionary<string, object>();
         {
            changes.Add("UtteranceLog", logItems);
         };

         try
         {
            // Save new list to your Storage.
            await _myStorage.WriteAsync(changes,cancellationToken);
         }
         catch
         {
            // Inform the user an error occurred.
            await turnContext.SendActivityAsync("Sorry, something went wrong storing your message!");
         }
      }
   }
}

ボットの起動

ボットをローカルで実行します。

エミュレーターの起動とボットの接続

Bot Framework Emulator をインストールしてから、エミュレーターを起動し、そのエミュレーターのボットに接続します。

  1. エミュレーターの [ようこそ] タブにある [新しいボット構成を作成する] リンクを選択します。
  2. ボットを起動したときに表示される Web ページの情報を踏まえ、ご自身のボットに接続するためのフィールドに入力します。

ボットでのやり取り

メッセージをお使いのボットに送信します。 ボットには、受信したメッセージが一覧表示されます。

A conversation with the bot that shows the bot keeping a list of messages from the user.

この記事の他の部分では、ボットの内部メモリではなく永続ストレージに保存する方法を示します。

Cosmos DB の使用

重要

Cosmos DB ストレージ クラスは非推奨となりました。 もともと CosmosDbStorage で作成されたコンテナーにはパーティション キーが設定されておらず、_/partitionKey の既定のパーティション キーが与えられています。

Cosmos DB ストレージで作成されたコンテナーは、Cosmos DB パーティション分割ストレージと共に使用できます。 詳細については、Azure Cosmos DB でのパーティション分割に関するページを参照してください。

また、従来の Cosmos DB ストレージとは異なり、Cosmos DB パーティション分割ストレージでは、Cosmos DB アカウント内にデータベースが自動的に作成されません。 新しいデータベースを手動で作成する必要がありますが、CosmosDbPartitionedStorage によってコンテナーが作成されるため、コンテナーの手動作成はスキップしてください。

メモリ ストレージを使用しているので、Azure Cosmos DB を使用するようにコードを更新します。 Cosmos DB は、Microsoft のグローバル分散型マルチモデル データベースです。 Azure Cosmos DB では、Azure のリージョンをいくつでもまたいでスループットとストレージを柔軟かつ個別にスケーリングすることができます。 このサービスは包括的なサービス レベル アグリーメント (SLA) により、スループット、待機時間、可用性、一貫性が保証されています。

Cosmos DB リソースをセットアップする

ボットで Cosmos DB を使用するには、コードに取り組む前に、データベース リソースを作成する必要があります。 Cosmos DB データベースとアプリの作成の詳細については、.NETNode.js、または Python のクイック スタートを参照してください。

データベース アカウントの作成

  1. Azure portal に移動して、Azure Cosmos DB アカウントを作成します。 [Azure Cosmos DB] を検索して選択します。

  2. [Azure Cosmos DB] ページで、[新規] を選択して [Azure Cosmos DB アカウントの作成] ページを表示します。

    Screenshot of creating your Cosmos DB account.

  3. 次のフィールドの値を指定します。

    1. [サブスクリプション]。 この Azure Cosmos アカウントに使用する Azure サブスクリプションを選択します。
    2. リソース グループ。 既存のリソース グループを選択するか、[新規作成] を選択して新しいリソース グループの名前を入力します。
    3. [アカウント名]。 自分の Azure Cosmos アカウントを識別するための名前を入力します。 指定した名前に documents.azure.com が付加されて URI が作成されるので、一意の名前を使用してください。 次のガイドラインに注意してください。
      • この名前は Azure 全体で一意である必要があります。
      • 名前は 3 から 31 文字の長さでなければなりません。
      • 名前に含めることができるのは、英小文字、数字、ハイフン (-) のみです。
    4. APIコア (SQL) を選択します。
    5. 場所。 データに最も高速にアクセスできるよう、お客様のユーザーに最も近い場所を選択します。
  4. [確認および作成] を選択します。

  5. 検証が完了したら、[作成] を選択します。

アカウントの作成には数分かかります。 ポータルに "Azure Cosmos DB アカウントが作成されました ページが表示されます。

データベースを追加する

Note

コンテナーは自分では作成しないでください。 ボットは、その内部の Cosmos DB クライアントを作成するときにそれを作成し、ボットの状態を格納するために正しく構成されていることを確認します。

  1. 新しく作成した Cosmos DB アカウントの [データ エクスプローラー] ページに移動し、[コンテナーの作成] ドロップダウンで [データベースの作成] を選択します。 ウィンドウの右側にパネルが表示され、新しいデータベースの詳細を入力することができます。

    Screenshot of creating your Cosmos DB database.

  2. 新しいデータベースの ID を入力し、必要に応じてスループットを設定し (これは後で変更できます)、最後に [OK] を選択してデータベースを作成します。 このデータベース ID は、後でボットを構成するときに使用するためにメモしておいてください。

  3. Cosmos DB アカウントとデータベースを作成したので、新しいデータベースを bot に統合するために、いくつかの値をコピーする必要があります。 これらの値を取得するには、Cosmos DB アカウントのデータベースの設定セクション内にある [キー] タブに移動します。 このページからは、URI (Cosmos DB エンドポイント) と主キー (承認キー) が必要になります。

これで、データベースを持つ Cosmos DB アカウントが作成され、ボットの設定で次の値を使用できるようになります。

  • URI
  • 主キー
  • データベース ID

Cosmos DB の構成情報を追加する

この記事の前の部分でメモした詳細情報を使用して、エンドポイント、承認キー、およびデータベース ID を設定します。 最後に、ボットの状態を格納するためにデータベース内に作成されるコンテナーに適切な名前を選択する必要があります。 次の例では、作成される Cosmos DB コンテナーの名前は "bot-storage" になります。

構成ファイルに以下の情報を追加します。

appsettings.json

"CosmosDbEndpoint": "<your-CosmosDb-URI>",
"CosmosDbAuthKey": "<your-primary-key>",
"CosmosDbDatabaseId": "<your-database-id>",
"CosmosDbContainerId": "bot-storage"

Cosmos DB パッケージのインストール

Cosmos DB に必要なパッケージがあることを確認します。

Microsoft.Bot.Builder.Azure NuGet パッケージをインストールします。 NuGet の使用方法の詳細については、「NuGet パッケージ マネージャーを使用して Visual Studio にパッケージをインストールして管理する」を参照してください。

Cosmos DB の実装

Note

バージョン 4.6 では、新しい Cosmos DB ストレージ プロバイダーである "Cosmos DB パーティション分割ストレージ" クラスが導入され、元の "Cosmos DB ストレージ" クラスは非推奨となりました。 Cosmos DB ストレージで作成されたコンテナーは、Cosmos DB パーティション分割ストレージと共に使用できます。 詳細については、Azure Cosmos DB でのパーティション分割に関するページを参照してください。

従来の Cosmos DB ストレージとは異なり、Cosmos DB パーティション分割ストレージでは、Cosmos DB アカウント内にデータベースが自動的に作成されることはありません。 新しいデータベースを手動で作成する必要がありますが、CosmosDbPartitionedStorage によってコンテナーが作成されるため、コンテナーの手動作成はスキップしてください。

次のサンプル コードは、上記のメモリ ストレージのサンプルと同じボット コードを使用して実行されます。このリストに例外を示します。 次のコード スニペットは、ローカルのメモリ ストレージを置き換える myStorage に対する Cosmos DB ストレージの実装を示しています。

まず、Bot Builder Azure ライブラリを参照するように Startup.cs を更新する必要があります。

using Microsoft.Bot.Builder.Azure;

次に、Startup.csConfigureServices メソッドで CosmosDbPartitionedStorage オブジェクトを作成します。 これは、依存関係の挿入によって EchoBot コンストラクターに渡されます。

// Use partitioned CosmosDB for storage, instead of in-memory storage.
services.AddSingleton<IStorage>(
    new CosmosDbPartitionedStorage(
        new CosmosDbPartitionedStorageOptions
        {
            CosmosDbEndpoint = Configuration.GetValue<string>("CosmosDbEndpoint"),
            AuthKey = Configuration.GetValue<string>("CosmosDbAuthKey"),
            DatabaseId = Configuration.GetValue<string>("CosmosDbDatabaseId"),
            ContainerId = Configuration.GetValue<string>("CosmosDbContainerId"),
            CompatibilityMode = false,
        }));

EchoBot.cs では、_myStorage 変数の宣言 private static readonly MemoryStorage _myStorage = new MemoryStorage(); を次のように変更します。

// variable used to save user input to CosmosDb Storage.
private readonly IStorage _myStorage;

次に、IStorage オブジェクトを EchoBot コンストラクターに渡します。

public EchoBot(IStorage storage)
{
    if (storage is null) throw new ArgumentNullException();
    _myStorage = storage;
}

Cosmos DB ボットを起動する

ボットをローカルで実行します。

Bot Framework Emulator での Cosmos DB ボットのテスト

Bot Framework Emulator を起動して、ボットに接続します。

  1. エミュレーターの [ようこそ] タブにある [新しいボット構成を作成する] リンクを選択します。
  2. ボットを起動したときに表示される Web ページの情報を踏まえ、ご自身のボットに接続するためのフィールドに入力します。

Cosmos DB ボットとの対話

メッセージをボットに送信します。ボットは受信したメッセージを表示します。

A conversation with the bot that shows the bot keeping a list of messages from the user.

Cosmos DB データを表示する

ボットを実行して情報を保存した後は、Azure portal の [データ エクスプローラー] タブで、格納データを表示できます。

Screenshot of the Data Explorer in the Azure portal.

Blob Storage の使用

Azure Blob Storage は、Microsoft のクラウド用オブジェクト ストレージ ソリューションです。 BLOB ストレージは、テキスト データやバイナリ データなどの大量の非構造化データを格納するために最適化されています。 このセクションでは、Azure BLOB ストレージ アカウントとコンテナーを作成する方法と、ボットから BLOB ストレージ コンテナーを参照する方法について説明します。

BLOB ストレージの詳細については、「Azure Blob Storage とは」を参照してください。

Blob Storage アカウントの作成

ボットで Blob Storage を使用するには、コードに取り組む前に、いくつかの設定を行う必要があります。

  1. Azure portal[すべてのサービス] を選択します。

  2. AI サービス ページの [おすすめ] セクションで、[ストレージ アカウント] を選択します。

  3. [ストレージ アカウント] ページで、 [新規] を選択します。

    Screenshot of creating an Azure Storage account.

  4. [サブスクリプション] フィールドで、ストレージ アカウントを作成するサブスクリプションを選択します。

  5. [リソース グループ] フィールドで、既存のリソース グループを選択するか、[新規作成] を選択し、新しいリソース グループの名前を入力します。

  6. [ストレージ アカウント名] で、アカウントの名前を入力します。 次のガイドラインに注意してください。

    • この名前は Azure 全体で一意である必要があります。
    • 名前は 3 から 24 文字の長さでなければなりません。
    • 名前には、数字と小文字のみを含めることができます。
  7. [場所] フィールドで、ストレージ アカウントの場所を選択するか、または既定の場所を使います。

  8. 残りの設定については、次のように構成します。

  9. [ストレージ アカウントの作成] ページの [プロジェクトの詳細] セクションで、サブスクリプションリソース グループに必要な値を選択します。

  10. [ストレージ アカウントの作成] ページの [インスタンスの詳細] セクションで、ストレージ アカウント名を入力し、[場所][アカウントの種類][レプリケーション] の値を選択します。

  11. [確認および作成] を選択して、ストレージ アカウントの設定を確認します。

  12. 検証が完了したら、[作成] を選択します。

Blob Storage コンテナーの作成

Blob storage アカウントが作成されたら、開いた後に次のことを行います。

  1. [Storage Explorer (プレビュー)] を選択します。

  2. 次に、[BLOB CONTAINERS] を 右クリックします

  3. ドロップダウン リストから [BLOB コンテナーの作成] を選択します。

    Screenshot of creating a blob container.

  4. [新しいコンテナー] フォームに名前を入力します。 この名前は、BLOB ストレージ アカウントへのアクセスを付与するための BLOB コンテナー名の値として使用します。 次のガイドラインに注意してください。

    • 名前には小文字、数字、ハイフンのみを含めることができます。
    • 名前の先頭は文字または数字にする必要があります。
    • 各ハイフンの前後にはハイフン以外の有効な文字を指定する必要があります。
    • 名前は 3 から 63 文字の長さでなければなりません。

Blob ストレージの構成情報を追加する

上記のボットの Blob Storage の構成に必要な Blob Storage キーを検索します。

  1. Azure portal で、Blob ストレージ アカウントを開き、[設定] セクションで [アクセス キー] を選択します。
  2. BLOB ストレージ アカウントにアクセスするようにボットを構成するには、BLOB 接続文字列の値に接続文字列を使用します。

構成ファイルに以下の情報を追加します。

appsettings.json

"BlobConnectionString": "<your-blob-connection-string>",
"BlobContainerName": "<your-blob-container-name>",

BLOB ストレージ パッケージのインストール

次のパッケージをインストールします (まだインストールされていない場合)。

Microsoft.Bot.Builder.Azure.Blobs NuGet パッケージをインストールします。 詳細については、「NuGet パッケージ マネージャーを使用して Visual Studio にパッケージをインストールして管理する」を参照してください。

BLOB ストレージの実装

BLOB ストレージ は、ボットの状態を格納するために使用されます。

Note

バージョン 4.10 時点で、Microsoft.Bot.Builder.Azure.AzureBlobStorage は非推奨となっています。 その代わりに新しいファイル Microsoft.Bot.Builder.Azure.Blobs.BlobsStorage を使用します。

次のサンプル コードは、上記のメモリ ストレージのサンプルと同じボット コードを使用して実行されます。このリストに例外を示します。

次のコード スニペットは、ローカルのメモリ ストレージを置き換える myStorage に対する Blob ストレージの実装を示しています。

まず、Bot Builder Azure blobs ライブラリを参照するように Startup.cs を更新する必要があります。

Startup.cs

using Microsoft.Bot.Builder.Azure.Blobs;

次に、Startup.csConfigureServices メソッドで BlobsStorage オブジェクトを作成し、appsettings.json の値を渡します。 これは、依存関係の挿入によって EchoBot コンストラクターに渡されます。

//Use Azure Blob storage, instead of in-memory storage.
services.AddSingleton<IStorage>(
    new BlobsStorage(
        Configuration.GetValue<string>("BlobConnectionString"),
        Configuration.GetValue<string>("BlobContainerName")
        ));

まず、Bot Builder Azure blobs ライブラリを参照するように EchoBot.cs を更新する必要があります。

EchoBot.cs

using Microsoft.Bot.Builder.Azure.Blobs;

次に、MemoryStorage 変数 'private static readonly MemoryStorage _myStorage = new MemoryStorage()' を作成するコード行を削除またはコメント アウトし、ユーザー入力を Blob Storage に保存するために使用する新しい変数を作成します。

EchoBot.cs

// variable used to save user input to CosmosDb Storage.
private readonly IStorage _myStorage;

次に、IStorage オブジェクトを EchoBot コンストラクターに渡します。

public EchoBot(IStorage storage)
{
    if (storage is null) throw new ArgumentNullException();
    _myStorage = storage;
}

ご自身の Blob Storage アカウントを指すようにストレージが設定されると、ボット コードは Blob Storage からデータを保存および取得するようになります。

ご自身の Blob Storage アカウントを指すようにストレージが設定されると、ボット コードは Blob Storage からデータを保存および取得するようになります。

Blob Storage ボットを起動する

ボットをローカルで実行します。

エミュレーターを起動し、Blob ストレージ ボットに接続します

次に、エミュレーターを起動し、エミュレーターでボットに接続します。

  1. エミュレーターの [ようこそ] タブにある [新しいボット構成を作成する] リンクを選択します。
  2. ボットを起動したときに表示される Web ページの情報を踏まえ、ご自身のボットに接続するためのフィールドに入力します。

Blob Storage ボットと対話する

メッセージをボットに送信すると、受信したメッセージがボットに一覧表示されます。

A conversation with the bot that shows the bot keeping a list of messages from the user.

Blob Storage データを表示する

ボットを実行して情報を保存したら、Azure portal の [ストレージ エクスプローラー] タブの下にその情報を表示できます。

Blob トランスクリプト ストレージ

Azure Blob トランスクリプト ストレージには、特殊なストレージ オプションが用意されています。このオプションを使用すると、記録されたトランスクリプトの形式でユーザーの会話を簡単に保存および取得できます。 Azure Blob トランスクリプト ストレージは、ボットのパフォーマンスをデバッグする際に、調べるユーザー入力を自動的にキャプチャする場合に便利です。

Note

Python は現在、Azure BLOB トランスクリプト ストレージをサポートしていません。 JavaScript では BLOB トランスクリプト ストレージがサポートされていますが、次の手順は C# のみを対象としています。

BLOB トランスクリプト ストレージ コンテナーを設定する

Azure Blob トランスクリプト ストレージでは、上記の「Blob Storage アカウントの作成」および「構成情報の追加」の手順に従って作成したのと同じ Blob Storage アカウントを使用できます。 ここで、トランスクリプトを保持するためのコンテナーを追加します

Screenshot of creating a blob container to use as a transcript store.

  1. Azure Blob Storage アカウントを開きます。
  2. Storage Explorer を選択します。
  3. "BLOB コンテナー" を右クリックし、"BLOB コンテナーの作成" を選択します。
  4. トランスクリプト コンテナーの名前を入力し、[OK] を選択します (mybottranscripts と入力しました)。

BLOB トランスクリプト ストレージの実装

次のコードでは、トランスクリプト ストレージのポインター _myTranscripts を新しい Azure Blob トランスクリプト ストレージ アカウントに接続します。 新しいコンテナー名 <your-blob-transcript-container-name> でこのリンクを作成するために、トランスクリプト ファイルを保持する新しいコンテナーが Blob Storage に作成されます。

BLOB トランスクリプト ストレージ は、ボットのトランスクリプトを格納するように設計されています。

Note

バージョン 4.10 時点で、Microsoft.Bot.Builder.Azure.AzureBlobTranscriptStore は非推奨となっています。 その代わりに新しいファイル Microsoft.Bot.Builder.Azure.Blobs.BlobsTranscriptStore を使用します。

echoBot.cs

using Microsoft.Bot.Builder.Azure.Blobs;

public class EchoBot : ActivityHandler
{
   ...

   private readonly BlobsTranscriptStore _myTranscripts = new BlobsTranscriptStore("<your-azure-storage-connection-string>", "<your-blob-transcript-container-name>");

   ...
}

Azure Blob トランスクリプトへのユーザーの会話の保存

BLOB コンテナーでトランスクリプトを保存できるようになったら、ボットとユーザーの会話の保持を開始できます。 このような会話を後からデバッグ ツールとして使用しすると、ユーザーがボットとどのようにやり取りするかを確認できます。 各エミュレーター "会話の再開" によって、新しいトランスクリプト会話リストの作成が開始されます。 次のコードでは、ユーザーの会話の入力を、保存されているトランスクリプト ファイルに保持します。

  • 現在のトランスクリプトは LogActivityAsync を使用して保存されます。
  • 保存されたトランスクリプトは ListTranscriptsAsync を使用して取得されます。 このサンプル コードでは、保存されている各トランスクリプトの ID が "storedTranscripts" という名前のリストに保存されます。 このリストは後で、保持する保存済み BLOB トランスクリプトの数を管理するときに使用します。

echoBot.cs


protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    await _myTranscripts.LogActivityAsync(turnContext.Activity);

    List<string> storedTranscripts = new List<string>();
    PagedResult<Microsoft.Bot.Builder.TranscriptInfo> pagedResult = null;
    var pageSize = 0;
    do
    {
       pagedResult = await _myTranscripts.ListTranscriptsAsync("emulator", pagedResult?.ContinuationToken);
       pageSize = pagedResult.Items.Count();

       // transcript item contains ChannelId, Created, Id.
       // save the channelIds found by "ListTranscriptsAsync" to a local list.
       foreach (var item in pagedResult.Items)
       {
          storedTranscripts.Add(item.Id);
       }
    } while (pagedResult.ContinuationToken != null);

    ...
}

保存済み BLOB トランスクリプトの管理

保存済みトランスクリプトをデバッグ ツールとして使用できると、保存済みトランスクリプトの数が時間経過と共に増加し、保持したい数を超えることがあります。 以下の追加コードでは、DeleteTranscriptAsync を使用して、取得された最新の 3 つのトランスクリプト項目を除くすべての項目を、BLOB トランスクリプト ストアから削除します。

echoBot.cs


protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    await _myTranscripts.LogActivityAsync(turnContext.Activity);

    List<string> storedTranscripts = new List<string>();
    PagedResult<Microsoft.Bot.Builder.TranscriptInfo> pagedResult = null;
    var pageSize = 0;
    do
    {
       pagedResult = await _myTranscripts.ListTranscriptsAsync("emulator", pagedResult?.ContinuationToken);
       pageSize = pagedResult.Items.Count();

       // transcript item contains ChannelId, Created, Id.
       // save the channelIds found by "ListTranscriptsAsync" to a local list.
       foreach (var item in pagedResult.Items)
       {
          storedTranscripts.Add(item.Id);
       }
    } while (pagedResult.ContinuationToken != null);

    // Manage the size of your transcript storage.
    for (int i = 0; i < pageSize; i++)
    {
       // Remove older stored transcripts, save just the last three.
       if (i < pageSize - 3)
       {
          string thisTranscriptId = storedTranscripts[i];
          try
          {
             await _myTranscripts.DeleteTranscriptAsync("emulator", thisTranscriptId);
           }
           catch (System.Exception ex)
           {
              await turnContext.SendActivityAsync("Debug Out: DeleteTranscriptAsync had a problem!");
              await turnContext.SendActivityAsync("exception: " + ex.Message);
           }
       }
    }
    ...
}

クラスの詳細については、「Azure Blob Transcript Storage」を参照してください。

追加情報

eTag を使用してコンカレンシーを管理する

ボット コード例では、各 IStoreItemeTag プロパティを * に設定します。 ストア オブジェクトの eTag (エンティティ タグ) メンバーは、コンカレンシーを管理するために Cosmos DB 内で使用されます。 eTag は、自分のボットが書き込んでいる同じストレージ内のオブジェクトをボットの別のインスタンスが変更した場合の対処方法をデータベースに通知します。

最後の書き込みが有効 - 上書きを許可する

eTag プロパティの値にアスタリスク (*) を指定すると、最後の書き込みが有効であることを示します。 新しいデータ ストアを作成するときは、プロパティの eTag* に設定して、以前に保存したことのないデータを書き込んでいること、または以前に保存されたすべてのプロパティを最後の書き込みで上書きすることを示します。 コンカレンシーがボットにとって問題にならない場合は、書き込んでいるすべてのデータについて eTag プロパティを * に設定して上書きを許可します。

コンカレンシーを維持して上書きを禁止する

データを Cosmos DB に保存する際に、プロパティへの同時アクセスを禁止し、変更をボットの別のインスタンスによって上書きされないようにする場合は、* 以外の値を eTag に使います。 ボットが状態データを保存しようとして、eTag がストレージ内の eTag と同じ値でない場合、ボットは etag conflict key= というメッセージのエラー応答を受け取ります。

既定では、Cosmos DB ストアはボットがストレージ オブジェクトを書き込むたびにその項目の eTag プロパティが等しいかどうかを確認し、各書き込みの後で新しい一意の値に更新します。 書き込みの eTag プロパティがストレージの eTag と一致しない場合は、別のボットまたはスレッドがデータを変更したことを意味します。

たとえば、保存されたメモをボットで編集しますが、ボットの別のインスタンスが行った変更を上書きしないようにする必要があるものとします。 ボットの別のインスタンスが編集を行った場合は、ユーザーに最新の更新でバージョンを編集させます。

最初に、IStoreItem を実装するクラスを作成します。

EchoBot.cs

public class Note : IStoreItem
{
    public string Name { get; set; }
    public string Contents { get; set; }
    public string ETag { get; set; }
}

次に、ストレージ オブジェクトを作成することによって最初のメモを作成し、オブジェクトをストアに追加します。

EchoBot.cs

// create a note for the first time, with a non-null, non-* ETag.
var note = new Note { Name = "Shopping List", Contents = "eggs", ETag = "x" };

var changes = Dictionary<string, object>();
{
    changes.Add("Note", note);
};
await NoteStore.WriteAsync(changes, cancellationToken);

後でメモにアクセスして更新し、ストアから読み取ったその eTag を維持します。

EchoBot.cs

var note = NoteStore.ReadAsync<Note>("Note").Result?.FirstOrDefault().Value;

if (note != null)
{
    note.Contents += ", bread";
    var changes = new Dictionary<string, object>();
    {
         changes.Add("Note1", note);
    };
    await NoteStore.WriteAsync(changes, cancellationToken);
}

変更を書き込む前にストアのメモが更新されていた場合、Write の呼び出しでは例外がスローされます。

コンカレンシーを維持するには、常にストレージからプロパティを読み取った後、読み取ったプロパティを変更して、eTag が維持されるようにします。 ストアからユーザー データを読み取る場合、応答には eTag プロパティが含まれます。 データを変更して更新後のデータをストアに書き込む場合、要求に含まれる eTag プロパティでは、前に読み取ったのと同じ値が指定されている必要があります。 ただし、eTag* に設定してオブジェクトを書き込むと、書き込みで他の変更を上書きできます。

次のステップ

ストレージを直接読み書きする方法がわかったので、次は状態マネージャーを使ってそれを行う方法を確認します。