您现在访问的是微软AZURE全球版技术文档网站,若需要访问由世纪互联运营的MICROSOFT AZURE中国区技术文档网站,请访问 https://docs.azure.cn.

直接写入存储Write directly to storage

适用于: SDK v4APPLIES TO: SDK v4

可以对存储对象进行直接读写,不需使用中间件或上下文对象。You can read and write directly to your storage object without using middleware or context object. 这可能适用于机器人用来保留聊天的数据,或者来源于机器人聊天流外部的数据。This can be appropriate for data your bot uses to preserve a conversation, or data that comes from a source outside your bot's conversation flow. 在此数据存储模型中,直接从存储中读取数据,而不使用状态管理器。In this data storage model, data is read in directly from storage instead of using a state manager. 本文中的代码示例演示了如何使用 内存Cosmos DBazure blobazure blob 脚本 存储在存储中读取和写入数据。The code examples in this article show you how to read and write data to storage using memory, Cosmos DB, Azure Blob, and Azure Blob transcript storage.

先决条件Prerequisites

备注

VSIX包包括 .net core 2.1 和 .net core 3.1 版本的 c # 模板。The VSIX package includes both .NET Core 2.1 and .NET Core 3.1 versions of the C# templates. 在 Visual Studio 2019 中创建新的机器人时,应使用 .NET Core 3.1 模板。When creating new bots in Visual Studio 2019, you should use the .NET Core 3.1 templates. 当前机器人示例使用 .NET Core 3.1 模板。The current bot samples use .NET Core 3.1 templates. 可在 BotBuilder-Samples 存储库的 4.7-存档分支中找到使用 .NET Core 2.1 模板的示例。You can find the samples that use .NET Core 2.1 templates in the 4.7-archive branch of the BotBuilder-Samples repository. 有关将 .NET Core 3.1 bot 部署到 Azure 的详细信息,请参阅如何将 机器人部署到 azureFor information about deploying .NET Core 3.1 bots to Azure, see how to deploy your bot to Azure.

关于此示例About this sample

本文中的示例代码首先演示基本聊天机器人的结构,然后通过添加更多的代码(下面会提供)来扩展该机器人的功能。The sample code in this article begins with the structure of a basic echo bot, then extends that bot's functionality by adding additional code (provided below). 此扩展代码会创建一个列表,用于在收到用户输入时保留这些输入。This extended code creates a list to preserve user inputs as they are received. 在每个轮次,会将完整的用户输入列表回显给用户。Each turn, the full list of user inputs is echoed back to the user. 在该轮次结束时,包含此输入列表的数据结构将保存到存储中。The data structure containing this list of inputs is then saved to storage at the end of that turn. 在此示例代码中添加了附加功能后,将探索各种类型的存储。Various types of storage are explored as additional functionality is added to this sample code.

内存存储Memory storage

Bot Framework SDK 允许使用内存中存储来存储用户输入。The Bot Framework SDK allows you to store user inputs using in-memory storage. 内存存储仅用于测试,不用于生产。Memory storage is used for testing purposes only and is not intended for production use. 内存中存储是易失、暂时性的,因为每次重启机器人时都会清除数据。In-memory storage is volatile and temporary since the data is cleared each time the bot is restarted. 持久性存储类型(例如数据库存储)最适合生产用机器人。Persistent storage types, such as database storage, are best for production bots. 请确保在发布机器人之前,将存储设置为“Cosmos DB”、“Blob 存储”或“Azure 表存储” 。Be sure to set storage to Cosmos DB, Blob Storage, or Azure Table storage before publishing your bot.

生成基础机器人Build a basic bot

本主题的其余部分在 Echo 机器人的基础上进行构建。The rest of this topic builds off of an Echo bot. 可以遵照有关生成 C# EchoBotJS EchoBotPython EchoBot 的快速入门说明,在本地生成聊天机器人示例代码。The Echo bot sample code can be locally built by following the Quickstart instructions for building either a C# EchoBot, JS EchoBot or Python EchoBot.

EchoBot.csEchoBot.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!");
         }
      }
      ...  // OnMessageActivityAsync( )
   }
}

启动机器人Start your bot

在本地运行机器人。Run your bot locally.

启动模拟器并连接机器人Start the Emulator and connect your bot

安装机器人框架 模拟器 接下来,启动模拟器,然后连接到模拟器中的机器人:Install the Bot Framework Emulator Next, start the Emulator and then connect to your bot in the Emulator:

  1. 在模拟器的 "欢迎" 选项卡中,单击 " 创建新的机器人配置 " 链接。Click the Create new bot configuration link in the Emulator "Welcome" tab.
  2. 填写用于连接到机器人的字段,并指定启动机器人时要在网页上显示的信息。Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot.

与机器人交互Interact with your bot

向机器人发送消息Send a message to your bot. 机器人会列出它所收到的消息。The bot will list the messages it has received.

在仿真器中测试存储机器人

使用 Cosmos DBUsing Cosmos DB

重要

Cosmos DB 存储类已弃用。The Cosmos DB storage class has been deprecated. 最初用 CosmosDbStorage 创建的容器没有设置分区键,并被赋予了 _ partitionKey 的默认分区键 / 。Containers originally created with CosmosDbStorage had no partition key set, and were given the default partition key of _/partitionKey.

使用 Cosmos DB 存储 创建的容器可以与 Cosmos DB 分区存储 一起使用。Containers created with Cosmos DB storage can be used with Cosmos DB partitioned storage. 有关详细信息,请阅读 Azure Cosmos DB 中的分区Read Partitioning in Azure Cosmos DB for more information.

另请注意,与旧的 Cosmos DB 存储不同,Cosmos DB 分区存储不会自动在 Cosmos DB 帐户中创建数据库。Also note that, unlike the legacy Cosmos DB storage, the Cosmos DB partitioned storage does not automatically create a database within your Cosmos DB account. 需要 手动创建新的数据库,但跳过手动创建容器,因为 CosmosDbPartitionedStorage 将为你创建容器。You need to create a new database manually, but skip manually creating a container since CosmosDbPartitionedStorage will create the container for you.

使用内存存储以后,我们现在要更新代码,以便使用 Azure Cosmos DB。Now that you've used memory storage, we'll update the code to use Azure Cosmos DB. Cosmos DB 由 Microsoft 推出的全球分布式多模型数据库。Cosmos DB is Microsoft's globally distributed, multi-model database. 使用 Azure Cosmos DB 可跨任意数量的 Azure 地理区域弹性且独立地缩放吞吐量和存储。Azure Cosmos DB enables you to elastically and independently scale throughput and storage across any number of Azure's geographic regions. 它通过综合服务级别协议 (SLA) 提供吞吐量、延迟、可用性和一致性保证。It offers throughput, latency, availability, and consistency guarantees with comprehensive service level agreements (SLAs).

设置 Cosmos DB 资源Set up a Cosmos DB resource

若要在机器人中使用 Cosmos DB,需在编写代码之前创建一个数据库资源。To use Cosmos DB in your bot, you'll need to create a database resource before getting into the code. 有关 Cosmos DB 数据库和应用创建过程的深入介绍,请访问适用于 Cosmos DB dotnetCosmos DB nodejs 的文档。For an in-depth description of Cosmos DB database and app creation access the documentation here for Cosmos DB dotnet or Cosmos DB nodejs.

创建数据库帐户Create your database account

  1. 在新浏览器窗口中,登录到 Azure 门户In a new browser window, sign in to the Azure portal.

    创建 Cosmos DB 数据库帐户

  2. 单击“创建资源”>“数据库”>“Azure Cosmos DB”Click Create a resource > Databases > Azure Cosmos DB

    Cosmos DB -“新建帐户”页

  3. 在“新建帐户”页上,提供“订阅”和“资源组”信息。 On the New account page, provide Subscription, Resource group information. 在“帐户名”字段中创建唯一的名称 - 此名称最终会成为数据访问 URL 名称的一部分。Create a unique name for your Account Name field - this eventually becomes part of your data access URL name. 对于“API”,请选择“Core(SQL)”,并提供一个附近的 位置 以加快数据访问速度。 For API, select Core(SQL), and provide a nearby Location to improve data access times.

  4. 然后单击“查看 + 创建”。Then click Review + Create.

  5. 验证成功后,单击“创建”。Once validation has been successful, click Create.

创建帐户需要几分钟时间。The account creation takes a few minutes. 等待门户中显示“祝贺你!Wait for the portal to display the Congratulations! 已创建 Azure Cosmos DB 帐户”页。Your Azure Cosmos DB account was created page.

添加数据库Add a database

重要

与现在已弃用的旧 Cosmos DB 存储 不同, Cosmos DB 分区存储 不会自动在 Cosmos DB 帐户中创建数据库。Unlike the legacy Cosmos DB storage, which has now been deprecated, the Cosmos DB partitioned storage does not automatically create a database within your Cosmos DB account.

  1. 在新建的 Cosmos DB 帐户中导航到“数据资源管理器”页,然后从“创建容器”按钮旁边的下拉框中选择“创建数据库”。 Navigate to the Data Explorer page within your newly created Cosmos DB account, then choose Create Database from the drop-down box next to the Create Container button. 随后,在窗口的右侧会打开一个面板,你可以在其中输入新数据库的详细信息。A panel will then open on the right hand side of the window, where you can enter the details for the new database.

    创建 cosmosdb 数据库资源映像

  2. 输入新数据库的 ID,并根据需要设置吞吐量(以后可以更改),最后单击“确定”以创建数据库。Enter an ID for your new database and, optionally, set the throughput (you can change this later) and finally click OK to create your database. 请记下此数据库 ID,以便稍后在配置机器人时使用。Make a note of this database ID for use later on when configuring your bot.

    Cosmos cosmosdb 数据库资源详细信息图像

  3. 创建 Cosmos DB 帐户和数据库后,接下来需要复制某些值,以便能够将新数据库集成到机器人中。Now that you have created a Cosmos DB account and a database, you need to copy over some of the values for integrating your new database into your bot. 若要检索这些值,请在 Cosmos DB 帐户的“数据库设置”部分导航到“密钥”选项卡。To retrieve these, navigate to the Keys tab within the database settings section of your Cosmos DB account. 在此页中,需要使用 Cosmos DB 终结点 (URI) 和授权密钥(主密钥)。From this page you will need your Cosmos DB endpoint (URI) and your authorization key (PRIMARY KEY).

    Cosmos DB 密钥

现在,应已创建一个包含数据库的 Cosmos DB 帐户,并已准备好用于配置机器人的以下详细信息。You should now have a Cosmos DB account, containing a database and have the following details ready to configure your bot.

  • Cosmos DB 终结点Cosmos DB Endpoint
  • 授权密钥Authorization Key
  • 数据库 IDDatabase ID

添加 Cosmos DB 配置信息Add Cosmos DB configuration information

用于添加 Cosmos DB 存储的配置数据较为简短。Our configuration data to add Cosmos DB storage is short and simple. 使用在本文前面部分记下的详细信息来设置终结点、授权密钥和数据库 ID。Use the details you made a note of in the previous part of this article to set your endpoint, authorization key and database ID. 最后,应该为在数据库中创建的用于存储机器人状态的容器选择一个适当的名称。Finally, you should choose an appropriate name for the container that will be created within your database to store your bot state. 在以下示例中,容器名为“bot-storage”。In the example below the container will be called "bot-storage".

备注

不要自行创建容器。You should not create the container yourself. 机器人将在创建其内部 Cosmos DB 客户端时创建该容器,并确保正确对其进行配置,使之能够存储机器人状态。Your bot will create it for you when creating its internal Cosmos DB client, ensuring it is configured correctly for storing bot state.

在配置文件中添加以下信息。Add the following information to your configuration file.

appsettings.jsonappsettings.json

"CosmosDbEndpoint": "<your-cosmosdb-uri>",
"CosmosDbAuthKey": "<your-authorization-key>",
"CosmosDbDatabaseId": "<your-database-id>",
"CosmosDbContainerId": "<your-container-id>"

安装 Cosmos DB 程序包Installing Cosmos DB packages

确保有 Cosmos DB 所需的包。Make sure you have the packages necessary for Cosmos DB.

Install-Package Microsoft.Bot.Builder.Azure

Cosmos DB 实现Cosmos DB implementation

备注

版本 4.6 引入了新的 Cosmos DB 存储提供程序,即 Cosmos DB 分区存储类,并且弃用了原来的 Cosmos DB 存储类。Version 4.6 introduced a new Cosmos DB storage provider, the Cosmos DB partitioned storage class, and the original Cosmos DB storage class is deprecated. 使用 Cosmos DB 存储 创建的容器可以与 Cosmos DB 分区存储 一起使用。Containers created with Cosmos DB storage can be used with Cosmos DB partitioned storage. 有关详细信息,请阅读 Azure Cosmos DB 中的分区Read Partitioning in Azure Cosmos DB for more information.

另请注意,与旧的 Cosmos DB 存储不同,Cosmos DB 分区存储不会自动在 Cosmos DB 帐户中创建数据库。Also note that, unlike the legacy Cosmos DB storage, the Cosmos DB partitioned storage does not automatically create a database within your Cosmos DB account. 需要 手动创建新的数据库,但跳过手动创建容器,因为 CosmosDbPartitionedStorage 将为你创建容器。You need to create a new database manually, but skip manually creating a container since CosmosDbPartitionedStorage will create the container for you.

以下示例代码运行时,使用的是与上面提供的内存存储示例相同的机器人代码。The following sample code runs using the same bot code as the memory storage sample provided above. 以下代码片段演示如何为“myStorage”实现 Cosmos DB 存储,替换本地内存存储。The code snippet below shows an implementation of Cosmos DB storage for 'myStorage' that replaces local Memory storage. 内存存储已注释掉,并已替换为对 Cosmos DB 的引用。Memory Storage is commented out and replaced with a reference to Cosmos DB.

Startup.csStartup.cs

using Microsoft.Bot.Builder.Azure;

ConfigureServices 中,为 CosmosDB 分区存储创建存储实例。Within ConfigureServices, create the storage instance for CosmosDB partitioned storage.

// 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,
        }));

启动 Cosmos DB 机器人Start your Cosmos DB bot

在本地运行机器人。Run your bot locally.

利用机器人 framework 模拟器测试 Cosmos DB 机器人Test your Cosmos DB bot with bot framework Emulator

现在启动机器人 framework 模拟器并连接到机器人:Now start your bot framework Emulator and connect to your bot:

  1. 在模拟器的 "欢迎" 选项卡中,单击 " 创建新的机器人配置 " 链接。Click the Create new bot configuration link in the Emulator "Welcome" tab.
  2. 填写用于连接到机器人的字段,并指定启动机器人时要在网页上显示的信息。Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot.

与 Cosmos DB 机器人交互Interact with your Cosmos DB bot

向机器人发送消息,机器人会列出所收到的消息。Send a message to your bot, and the bot will list the messages it received. 模拟器运行Emulator running

查看 Cosmos DB 数据View your Cosmos DB data

运行机器人并保存信息以后,即可在 Azure 门户的“数据资源管理器”选项卡下查看存储的数据。After you have run your bot and saved your information, we can view the data stored in the Azure portal under the Data Explorer tab.

数据资源管理器示例

使用 Blob 存储Using Blob storage

Azure Blob 存储是 Microsoft 提供的适用于云的对象存储解决方案。Azure Blob storage is Microsoft's object storage solution for the cloud. Blob 存储最适合存储巨量的非结构化数据,例如文本或二进制数据。Blob storage is optimized for storing massive amounts of unstructured data, such as text or binary data.

创建 Blob 存储帐户Create your Blob storage account

若要在机器人中使用 Blob 存储,需在编写代码之前进行一些设置。To use Blob storage in your bot, you'll need to get a few things set up before getting into the code.

  1. 在新浏览器窗口中,登录到 Azure 门户In a new browser window, sign in to the Azure portal.

    创建 Blob 存储

  2. 单击“创建资源”>“存储”>“存储帐户 - Blob、文件、表、队列”Click Create a resource > Storage > Storage account - blob, file, table, queue

    Blob 存储 -“新建帐户”页

  3. 在“新建帐户”页中输入存储帐户的“名称”,选择“Blob 存储”作为“帐户类型”,提供“位置”、“资源组”和“订阅”信息 。In the New account page, enter Name for the storage account, select Blob storage for Account kind, provide Location, Resource group and Subscription information.

  4. 然后单击“查看 + 创建”。Then click Review + Create.

  5. 验证成功后,单击“创建”。Once validation has been successful, click Create.

创建 Blob 存储容器Create Blob storage container

创建 Blob 存储帐户后,通过以下方式打开此帐户Once your Blob storage account is created, open this account by

  1. 选择资源。Selecting the resource.

  2. 在存储资源管理器(预览版)中选择“打开”Now "Open" using Storage Explorer (preview)

    创建 Blob 存储容器

  3. 右键单击“BLOB 容器”,并选择“创建 Blob 容器”。Right click BLOB CONTAINERS, select Create blob container.

  4. 添加一个名称。Add a name. 稍后将使用此名称作为“your-blob-storage-container-name”值来提供对 Blob 存储帐户的访问。You will use this name for the value "your-blob-storage-container-name" to provide access to your Blob Storage account.

添加 Blob 存储配置信息Add Blob storage configuration information

找到所需的 Blob 存储密钥,以便为机器人配置 Blob 存储,如上所示:Find the Blob Storage keys you need to configure Blob Storage for your bot as shown above:

  1. 在 Azure 门户中打开 Blob 存储帐户,然后选择“设置”>“访问密钥”。In the Azure portal, open your Blob Storage account and select Settings > Access keys.

    找到 Blob 存储密钥

我们将使用密钥 1“连接字符串”作为“your-blob-storage-container-name”值来提供对 Blob 存储帐户的访问。We will use key1 Connection string as the value "your-blob-storage-account-string" to provide access to your Blob Storage account.

安装 Blob 存储包Installing Blob storage packages

如果之前未安装,请安装以下程序包。If not previously installed, install the following packages.

Install-Package Microsoft.Bot.Builder.Azure.Blobs

Blob 存储实现Blob storage implementation

Blob 存储 旨在存储机器人状态。Blob storage is designed to store bot state.

备注

在版本4.10 中, Microsoft.Bot.Builder.Azure.AzureBlobStorage 已弃用。As of version 4.10, Microsoft.Bot.Builder.Azure.AzureBlobStorage is deprecated. 使用新的 Microsoft.Bot.Builder.Azure.Blobs.BlobsStorage 位置。Use the new Microsoft.Bot.Builder.Azure.Blobs.BlobsStorage in its place.

EchoBot.csEchoBot.cs

using Microsoft.Bot.Builder.Azure.Blobs;

更新将“myStorage”指向现有 Blob 存储帐户的代码行。Update the line of code that points "myStorage" to your existing Blob Storage account.

EchoBot.csEchoBot.cs

private static readonly BlobsStorage _myStorage = new BlobsStorage("<your-azure-storage-connection-string>", "<your-blob-storage-container-name>");

将存储设置为指向 Blob 存储帐户之后,机器人代码可以通过 Blob 存储来存储和检索数据。Once your storage is set to point to your Blob Storage account, your bot code will now store and retrieve data from Blob Storage.

启动 Blob 存储机器人Start your Blob storage bot

在本地运行机器人。Run your bot locally.

启动模拟器并连接 Blob 存储机器人Start the Emulator and connect your Blob storage bot

接下来,启动模拟器,然后连接到模拟器中的机器人:Next, start the Emulator and then connect to your bot in the Emulator:

  1. 在模拟器的 "欢迎" 选项卡中,单击 " 创建新的机器人配置 " 链接。Click the Create new bot configuration link in the Emulator "Welcome" tab.
  2. 填写用于连接到机器人的字段,并指定启动机器人时要在网页上显示的信息。Fill in fields to connect to your bot, given the information on the webpage displayed when you started your bot.

与 Blob 存储机器人交互Interact with your Blob storage bot

向机器人发送消息,机器人会列出所收到的消息。Send a message to your bot, and the bot will list the messages it receives.

在仿真器中测试存储机器人

查看 Blob 存储数据View your Blob storage data

运行机器人并保存信息以后,即可在 Azure 门户的“存储资源管理器”选项卡下查看。After you have run your bot and saved your information, we can view it in under the Storage Explorer tab in the Azure portal.

Blob 脚本存储Blob transcript storage

Azure Blob 脚本存储提供专门的存储选项,可以轻松地以记录脚本形式保存和检索用户聊天内容。Azure blob transcript storage provides a specialized storage option that allows you to easily save and retrieve user conversations in the form of a recorded transcript. Azure Blob 脚本存储尤其适合在调试机器人性能时自动捕获要检查的用户输入。Azure blob transcript storage is particularly useful for automatically capturing user inputs to examine while debugging your bot's performance.

备注

Python 目前不支持 Azure Blob 记录存储Python does not currently support Azure Blob transcript storage. 虽然 JavaScript 支持 Blob 记录存储,但以下方向仅适用于 c #。While JavaScript supports Blob transcript storage, the following directions are for C# only.

设置 Blob 脚本存储容器Set up a Blob transcript storage container

Azure Blob 脚本存储可以使用通过上面的“创建 Blob 存储帐户”和“添加配置信息”部分详述的步骤创建的 Blob 存储帐户。 Azure blob transcript storage can use the same blob storage account created following the steps detailed in sections "Create your blob storage account" and "Add configuration information" above. 现在添加一个容器用于保存脚本We now add a container to hold our transcripts

创建脚本容器

  1. 打开 Azure Blob 存储帐户。Open your Azure blob storage account.
  2. 单击“存储资源管理器”。Click on Storage Explorer.
  3. 右键单击“BLOB 容器”,并选择“创建 Blob 容器”。 Right click on BLOB CONTAINERS and select create blob container.
  4. 输入脚本容器的名称,然后选择“确定”。enter a name for your transcript container and then select OK. (此处输入了 mybottranscripts)(We entered mybottranscripts)

Blob 记录存储实现Blob transcript storage implementation

以下代码将脚本存储指针 _myTranscripts 连接到新的 Azure Blob 脚本存储帐户。The following code connects transcript storage pointer _myTranscripts to your new Azure blob transcript storage account. 若要使用新容器名称创建此链接, <your-blob-transcript-container-name> 请在 Blob 存储中创建一个新容器以保存脚本文件。To create this link with a new container name, <your-blob-transcript-container-name>, creates a new container within Blob storage to hold your transcript files.

Blob 记录存储 设计用于存储 bot 脚本。Blob transcript storage is designed to store bot transcripts.

备注

在版本4.10 中, Microsoft.Bot.Builder.Azure.AzureBlobTranscriptStore 已弃用。As of version 4.10, Microsoft.Bot.Builder.Azure.AzureBlobTranscriptStore is deprecated. 使用新的 Microsoft.Bot.Builder.Azure.Blobs.BlobsTranscriptStore 位置。Use the new Microsoft.Bot.Builder.Azure.Blobs.BlobsTranscriptStore in its place.

echoBot.csechoBot.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 脚本中存储用户聊天内容Store user conversations in azure blob transcripts

可以使用 Blob 容器来存储脚本以后,即可保留用户与机器人的聊天内容。After a blob container is available to store transcripts you can begin to preserve your users' conversations with your bot. 这些聊天内容可以在以后用作调试工具,以便了解用户与机器人的交互情况。These conversations can later be used as a debugging tool to see how users interact with your bot. 每个模拟器 重新启动会话 将开始创建新的脚本对话列表。Each Emulator Restart conversation initiates the creation of a new transcript conversation list. 以下代码将在存储的脚本文件中保留用户聊天输入。The following code preserves user conversation inputs within a stored transcript file.

  • 当前脚本是使用 LogActivityAsync 保存的。The current transcript is saved using LogActivityAsync.
  • 可以使用 ListTranscriptsAsync 检索已保存的脚本。Saved transcripts are retrieved using ListTranscriptsAsync. 在此示例代码中,存储的每个脚本的 ID 将保存到名为“storedTranscripts”的列表。In this sample code the Id of each stored transcript is saved into a list named "storedTranscripts". 稍后将使用此列表来管理保留的已存储 Blob 脚本数。This list is later used to manage the number of stored blob transcripts we retain.

echoBot.csechoBot.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 脚本Manage stored blob transcripts

尽管存储的脚本可用作调试工具,但一段时间后,存储的脚本数可能会大大增加,以致难以照料。While stored transcripts can be used as a debugging tool, over time the number of stored transcripts can grow larger than you care to preserve. 下面提供的附加代码使用 DeleteTranscriptAsync 从 Blob 脚本存储中删除除最后三个检索到的脚本项以外的其他所有脚本。The additional code included below uses DeleteTranscriptAsync to remove all but the last three retrieved transcript items from your blob transcript store.

echoBot.csechoBot.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 脚本存储的详细信息The following link provides more information concerning Azure Blob Transcript Storage

其他信息Additional Information

使用 eTag 管理并发Manage concurrency using eTags

在机器人代码示例中,我们将每个 IStoreItemeTag 属性设置为 *In our bot code example we set the eTag property of each IStoreItem to *. 存储对象的 eTag(实体标记)成员在 Cosmos DB 中用于管理并发。The eTag (entity tag) member of your store object is used within Cosmos DB to manage concurrency. 如果在机器人向存储中写入内容时,该机器人的另一实例更改了同一存储中的对象,eTag 会指示数据库应该如何处理。The eTag tells your database what to do if another instance of the bot has changed the object in the same storage that your bot is writing to.

最后一次写入才算 - 允许覆盖Last write wins - allow overwrites

星号 (*) 的 eTag 属性值指示最后一次写入才算数。An eTag property value of asterisk (*) indicates that the last writer wins. 在创建新的数据存储时,可以将属性的 eTag 设置为 *,以表明以前未保存正在编写的数据,或者想要最后一次写入覆盖任何以前保存的属性。When creating a new data store, you can set eTag of a property to * to indicate that you have not previously saved the data that you are writing, or that you want the last writer to overwrite any previously saved property. 如果并发性对于机器人来说不是问题,则对于任何要写入的数据,将 eTag 属性设置为 * 就可以允许覆盖。If concurrency is not an issue for your bot, setting the eTag property to * for any data that you are writing enables overwrites.

保持并发并防止覆盖Maintain concurrency and prevent overwrites

将数据存储到 Cosmos DB 中时,若要防止对属性进行并发访问并避免覆盖另一个机器人实例的更改,可以对 eTag 使用 * 之外的值。When storing your data into Cosmos DB, use a value other than * for the eTag if you want to prevent concurrent access to a property and avoid overwriting changes from another instance of the bot. 当机器人尝试保存状态数据但 eTag 的值与存储中 eTag 的值不同时,它会收到包含 etag conflict key= 消息的错误响应。The bot receives an error response with the message etag conflict key= when it attempts to save state data and the eTag is not the same value as the eTag in storage.

默认情况下,每当机器人写入到该项时,Cosmos DB 存储都会检查存储对象的 eTag 属性是否等同,然后在每次写入之后将其更新为新的惟一值。By default, the Cosmos DB store checks the eTag property of a storage object for equality every time a bot writes to that item, and then updates it to a new unique value after each write. 如果写入的 eTag 属性与存储中的 eTag 不匹配,这意味着另一个机器人或线程已更改数据。If the eTag property on write doesn't match the eTag in storage, it means another bot or thread changed the data.

例如,假设你想让机器人编辑已保存的注释,但不希望机器人覆盖另一个机器人实例所做的更改。For example, let's say you want your bot to edit a saved note, but you don't want your bot to overwrite changes that another instance of the bot has done. 如果另一个机器人实例已进行了编辑,你希望用户使用最新更新编辑版本。If another instance of the bot has made edits, you want the user to edit the version with the latest updates.

首先,创建实现 IStoreItem 的类。First, create a class that implements IStoreItem.

EchoBot.csEchoBot.cs

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

接下来,通过创建存储对象创建初始注释,并将该对象添加到存储。Next, create an initial note by creating a storage object, and add the object to your store.

EchoBot.csEchoBot.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);

然后访问和更新注释,保留从存储中读取的 eTagThen, access and update the note later, keeping its eTag that you read from the store.

EchoBot.csEchoBot.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 将引发异常。If the note was updated in the store before you write your changes, the call to Write will throw an exception.

若要保持并发,始终从存储读取属性,然后修改读取的属性,以便保持 eTagTo maintain concurrency, always read a property from storage, then modify the property you read, so that the eTag is maintained. 如果从存储中读取用户数据,响应将包含 eTag 属性。If you read user data from the store, the response will contain the eTag property. 如果更改数据并将更新后的数据写入到存储中,请求应包含指定与之前读取值相同的值的 eTag 属性。If you change the data and write updated data to the store, your request should include the eTag property that specifies the same value as you read earlier. 但是,编写将其 eTag 设置为 * 的对象将允许写入覆盖任何其他更改。However, writing an object with its eTag set to * will allow the write to overwrite any other changes.

后续步骤Next steps

现在你已经了解如何直接从存储中进行读写,接下来让我们来看看如何使用状态管理器来执行这些操作。Now that you know how to read read and write directly from storage, lets take a look at how you can use the state manager to do that for you.