Botframework V4: конфигурация Cosmos DB

Здравствуйте, мне трудно настроить базу данных Cosmos для работы с botframework. Прежде чем использовать хранилище памяти, она работает нормально. Я читаю это и это в качестве руководства. Я включил ошибки с комментариями в коды. Кто-нибудь может мне с этим помочь. Буду очень признателен за помощь. Я изучаю это уже 3 дня. Спасибо!

 public class Startup
{
    private const string CosmosServiceEndpoint = "xxxxxxxxxxx";
    private const string CosmosDBKey = "xxxxxxxxxxx";
    private const string CosmosDBDatabaseName = "xxxxxxxxxxx";
    private const string CosmosDBCollectionNameConState = "conversationState";
    private const string CosmosDBCollectionNameUserState = "userState";

    private ILoggerFactory _loggerFactory;
    private bool _isProduction = false;

    public Startup(IHostingEnvironment env)
    {
        _isProduction = env.IsProduction();

        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddBot<BasicBot>(options =>
        {
            ILogger logger = _loggerFactory.CreateLogger<BasicBot>();

            var secretKey = Configuration.GetSection("botFileSecret")?.Value;
            var botFilePath = Configuration.GetSection("botFilePath")?.Value;
            if (!File.Exists(botFilePath))
            {
                throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
            }

            BotConfiguration botConfig = null;
            try
            {
                botConfig = BotConfiguration.Load(botFilePath ?? @".\echo-with-counter.bot", secretKey);
            }
            catch
            {
                var msg = @"Error reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.
                - You can find the botFilePath and botFileSecret in the Azure App Service application settings.
                - If you are running this bot locally, consider adding a appsettings.json file with botFilePath and botFileSecret.
                - See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration.
                ";
                logger.LogError(msg);
                throw new InvalidOperationException(msg);
            }

            services.AddSingleton(sp => botConfig);

            var environment = _isProduction ? "production" : "development";
            var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
            if (service == null && _isProduction)
            {
                service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == "development").FirstOrDefault();
                logger.LogWarning("Attempting to load development endpoint in production environment.");
            }

            if (!(service is EndpointService endpointService))
            {
                throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
            }

            options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);

            options.OnTurnError = async (context, exception) =>
            {
                logger.LogError($"Exception caught : {exception}");
                await context.SendActivityAsync("Sorry, it looks like something went wrong.");
            };

            // The Memory Storage used here is for local bot debugging only. When the bot
            // is restarted, everything stored in memory will be gone.
            // IStorage dataStore = new MemoryStorage();

           // error : COSMOSDBSTORAGE DOES NOT CONTAIN CONSTRUCTOR TAKES 4 ARGUMENTS
           //IStorage dataStoreConversationState =
            // new CosmosDbStorage(
            //     uri,
            //     "** auth key **",
            //     "helloworldbot",
            //     "conversationstate");

            var uri = new Uri(CosmosServiceEndpoint);

            IStorage dataStoreConversationState =
            new CosmosDbStorage(new CosmosDbStorageOptions
            {
                AuthKey = CosmosDBKey,
                CollectionId = CosmosDBCollectionNameConState,
                CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
                DatabaseId = CosmosDBDatabaseName,
            });

            IStorage dataStoreUserState =
            new CosmosDbStorage(new CosmosDbStorageOptions
            {
                AuthKey = CosmosDBKey,
                CollectionId = CosmosDBCollectionNameUserState,
                CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
                DatabaseId = CosmosDBDatabaseName,
            });

           //error : THE NON GENERIC TYPE "CONVERSATIONsTATE" CANNOT BE USED WITH TYPED ARGUMENTS
            options.Middleware.Add(new ConversationState<BasicState>(dataStoreConversationState));
            options.Middleware.Add(new UserState<BasicUserState>(dataStoreUserState));

    }
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
1 369
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Есть большая вероятность, что причина, по которой это не работает для вас, заключается в том, что обе эти ссылки упоминают, что вам нужно создать новую коллекцию в вашем ресурсе CosmosDB в Azure. Microsoft недавно обновила ресурс CosmosDB, чтобы требовать создания новых коллекций с ключами разделов, которые еще не поддерживаются в Bot Framework. В настоящее время существует Запрос на изменение дизайна для добавления этой возможности, но она заблокирована C# Cosmos SDK.

А пока начните с создания ресурса Cosmos в Azure и НЕ создавайте базу данных или коллекцию. Делайте ТОЛЬКО ресурс Космос. SDK платформы бота настроен на создание новой БД и коллекции, если та, которую вы указали, не существует, и она может создать ее без разделов... так что пусть бот выполнит эту работу здесь.

Я использовал вторая ссылка, которую вы разместили, чтобы изменить Простой пример бота Prompt для работы с Cosmos. Примечание. endpoint и key используются по умолчанию для эмулятор CosmosDB, которые вы можете использовать для локального тестирования, если хотите.

Вот мой файл startup.cs:

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Integration;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Configuration;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.BotBuilderSamples
{
    /// <summary>
    /// The Startup class configures services and the app's request pipeline.
    /// </summary>
    public class Startup
    {
        private const string CosmosServiceEndpoint = "https://localhost:8081";
        private const string CosmosDBKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw= = ";
        private const string CosmosDBDatabaseName = "bot-cosmos-sql-db";
        private const string CosmosDBCollectionName = "bot-storage";

        private static readonly CosmosDbStorage _myStorage = new CosmosDbStorage(new CosmosDbStorageOptions
        {
            AuthKey = CosmosDBKey,
            CollectionId = CosmosDBCollectionName,
            CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
            DatabaseId = CosmosDBDatabaseName,
        });

        private ILoggerFactory _loggerFactory;
        private bool _isProduction = false;

        public Startup(IHostingEnvironment env)
        {
            _isProduction = env.IsProduction();

            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();

            Configuration = builder.Build();
        }

        /// <summary>
        /// Gets the configuration that represents a set of key/value application configuration properties.
        /// </summary>
        /// <value>
        /// The <see cref = "IConfiguration"/> that represents a set of key/value application configuration properties.
        /// </value>
        public IConfiguration Configuration { get; }

        /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// </summary>
        /// <param name = "services">The <see cref = "IServiceCollection"/> specifies the contract for a collection of service descriptors.</param>
        /// <seealso cref = "IStatePropertyAccessor{T}"/>
        /// <seealso cref = "https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection"/>
        /// <seealso cref = "https://docs.microsoft.com/en-us/azure/bot-service/bot-service-manage-channels?view=azure-bot-service-4.0"/>
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddBot<SimplePromptBot>(options =>
            {
                var secretKey = Configuration.GetSection("botFileSecret")?.Value;
                var botFilePath = Configuration.GetSection("botFilePath")?.Value;
                if (!File.Exists(botFilePath))
                {
                    throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}");
                }

                // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
                var botConfig = BotConfiguration.Load(botFilePath ?? @".\simple-prompt.bot", secretKey);
                services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded. botFilePath: {botFilePath}"));

                // Retrieve current endpoint.
                var environment = _isProduction ? "production" : "development";
                var service = botConfig.Services.FirstOrDefault(s => s.Type == "endpoint" && s.Name == environment);
                if (!(service is EndpointService endpointService))
                {
                    throw new InvalidOperationException($"The .bot file does not contain an endpoint with name '{environment}'.");
                }

                options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);

                // Creates a logger for the application to use.
                ILogger logger = _loggerFactory.CreateLogger<SimplePromptBot>();

                // Catches any errors that occur during a conversation turn and logs them.
                options.OnTurnError = async (context, exception) =>
                {
                    logger.LogError($"Exception caught : {exception}");
                    await context.SendActivityAsync("Sorry, it looks like something went wrong.");
                };

                // Memory Storage is for local bot debugging only. When the bot
                // is restarted, everything stored in memory will be gone.
                //IStorage dataStore = new MemoryStorage();

                // For production bots use the Azure Blob or
                // Azure CosmosDB storage providers. For the Azure
                // based storage providers, add the Microsoft.Bot.Builder.Azure
                // Nuget package to your solution. That package is found at:
                // https://www.nuget.org/packages/Microsoft.Bot.Builder.Azure/
                // Uncomment the following lines to use Azure Blob Storage
                // //Storage configuration name or ID from the .bot file.
                // const string StorageConfigurationId = "<STORAGE-NAME-OR-ID-FROM-BOT-FILE>";
                // var blobConfig = botConfig.FindServiceByNameOrId(StorageConfigurationId);
                // if (!(blobConfig is BlobStorageService blobStorageConfig))
                // {
                //    throw new InvalidOperationException($"The .bot file does not contain an blob storage with name '{StorageConfigurationId}'.");
                // }
                // // Default container name.
                // const string DefaultBotContainer = "<DEFAULT-CONTAINER>";
                // var storageContainer = string.IsNullOrWhiteSpace(blobStorageConfig.Container) ? DefaultBotContainer : blobStorageConfig.Container;
                // IStorage dataStore = new Microsoft.Bot.Builder.Azure.AzureBlobStorage(blobStorageConfig.ConnectionString, storageContainer);

                // Create Conversation State object.
                // The Conversation State object is where we persist anything at the conversation-scope.
                var conversationState = new ConversationState(_myStorage);

                options.State.Add(conversationState);
            });

            services.AddSingleton(sp =>
            {
                // We need to grab the conversationState we added on the options in the previous step.
                var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
                if (options == null)
                {
                    throw new InvalidOperationException("BotFrameworkOptions must be configured prior to setting up the State Accessors");
                }

                var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
                if (conversationState == null)
                {
                    throw new InvalidOperationException("ConversationState must be defined and added before adding conversation-scoped state accessors.");
                }

                // The dialogs will need a state store accessor. Creating it here once (on-demand) allows the dependency injection
                // to hand it to our IBot class that is create per-request.
                var accessors = new SimplePromptBotAccessors(conversationState)
                {
                    ConversationDialogState = conversationState.CreateProperty<DialogState>("DialogState"),
                };

                return accessors;
            });
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            _loggerFactory = loggerFactory;

            app.UseDefaultFiles()
                .UseStaticFiles()
                .UseBotFramework();
        }
    }
}

Вот разница, чтобы вы могли легко увидеть различия в коде.

Вот скриншот его работы:

Похоже, ваш код также хранит состояние пользователя и состояние разговора в отдельных коллекциях. Я думаю, что это работает... но "обычный" метод заключается в создании только одного экземпляра CosmosDbStorage. Бот будет хранить состояние пользователя и состояние диалога в отдельных документах коллекции. Обратите внимание, что в дополнение к приведенному выше коду вам, вероятно, понадобится что-то вроде var userState = new UserState(_myStorage), поскольку ваш код также использует userState, а приведенный выше код — нет.

Кроме того, и в соответствии с ответом Дрю, я думаю, что код из тот учебник, который вы связали может вызывать некоторые проблемы просто потому, что он устарел. Лучше всего было бы найти соответствующий образец из репозитория GitHub и использовать его в качестве ориентира. Базовый бот — это хороший вариант с функциями разговора и состояния пользователя.

Мне удалось заставить его работать, выполнив то же самое, что и в приведенном выше ответе. Спасибо за ваш ответ, сэр, я не знал, что есть эмулятор Cosmos db. это поможет мне в будущем.

user10860402 18.02.2019 04:15
Ответ принят как подходящий

Ваш код создает впечатление, что, возможно, вы когда-то писали какой-то код, используя предварительные версии, или, возможно, скопировали их у кого-то. ConversationState сам по себе больше не является ни промежуточным программным обеспечением, ни универсальным классом.

Таким образом, вы больше не создаете ConversationState<T> для каждой части состояния, которую хотите сохранить. Вместо этого вы создаете один ConversationState, который действует как своего рода «сегмент» области, если хотите, а затем вы создаете множество свойств в этом «сегменте», используя CreateProperty<T> API.

Нравится:

var conversationState = new ConversationState(myStorage);

var myBasicStateProperty conversationState.CreateProperty<BasicState>("MyBasicState");

Теперь, как я уже сказал, это уже не промежуточное ПО. Вместо этого из CreateProperty<T> возвращается IStatePropertyAccessor<T>, который вы затем можете передать тому, что нужно для его использования (например, вашему боту). Точно так же вы также передадите саму ConversationState боту, чтобы он мог в конечном итоге вызвать SaveChangesAsync в конце хода. В качестве альтернативы вы можете настроить AutoSaveStateMiddleware, который позаботится о сохранении состояния для вас в конце каждого хода, но вы потеряете контроль над способностью обрабатывать исключения, возникающие во время вызова SaveChangesAsync (например, сетевой раздел, параллелизм данных и т. д.). ).

У меня есть еще один сложный диалог, сэр Дрю. можешь проверить stackoverflow.com/questions/54758466/…

user10860402 19.02.2019 04:34

Другие вопросы по теме