Здравствуйте, мне трудно настроить базу данных 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));
}





Есть большая вероятность, что причина, по которой это не работает для вас, заключается в том, что обе эти ссылки упоминают, что вам нужно создать новую коллекцию в вашем ресурсе 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 и использовать его в качестве ориентира. Базовый бот — это хороший вариант с функциями разговора и состояния пользователя.
Ваш код создает впечатление, что, возможно, вы когда-то писали какой-то код, используя предварительные версии, или, возможно, скопировали их у кого-то. 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/…
Мне удалось заставить его работать, выполнив то же самое, что и в приведенном выше ответе. Спасибо за ваш ответ, сэр, я не знал, что есть эмулятор Cosmos db. это поможет мне в будущем.