Reputation: 4616
I am using Bot Framework SDK v-4. I have created a couple of dialogs inherited from ComponentDialog
and overridden the methods: BeginDialogAsync
and ContinueDialogAsync
. Below is the implementation of the IBot
.
public class Bot : IBot
{
private readonly BotAccessors _accessors;
private readonly ILogger _logger;
private DialogSet _dialogs = null;
private string _botName = string.Empty;
private IConfiguration _configuration = null;
private UserDetail _userDetail = null;
private ConversationData _conversationData = null;
private IStatePropertyAccessor<TurnState> _turnStateAccessor = null;
/// <summary>
/// Initializes a new instance of the class.
/// </summary>
public Bot(BotAccessors accessors, ILoggerFactory loggerFactory, IConfiguration configuration)
{
_accessors = accessors ?? throw new System.ArgumentNullException(nameof(accessors));
if (loggerFactory == null)
{
throw new System.ArgumentNullException(nameof(loggerFactory));
}
_configuration = configuration;
_dialogs = new DialogSet(accessors.ConversationState.CreateProperty<DialogState>(nameof(DialogState)));
_turnStateAccessor = accessors.TurnStateAccessor;
_dialogs.Add(new GreetingDialog(_turnStateAccessor, loggerFactory, configuration));
_dialogs.Add(new QnADialog(_turnStateAccessor, loggerFactory, configuration));
_botName = configuration["BotName"];
_logger = loggerFactory.CreateLogger<Bot>();
_logger.LogTrace("Bot turn start.");
}
public override Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
_turnState = _turnStateAccessor.GetAsync(outerDc.Context).Result;
outerDc.ContinueDialogAsync();
return base.BeginDialogAsync(outerDc, options, cancellationToken);
}
public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default(CancellationToken))
{
_turnState = _turnStateAccessor.GetAsync(outerDc.Context).Result;
//some code
_turnStateAccessor.SetAsync(outerDc.Context, _turnState).ConfigureAwait(false);
return base.ContinueDialogAsync(outerDc, cancellationToken);
}
}
I call the dialog from the OnTurnAsync
method of the Bot like this: await dialogContext.BeginDialogAsync(nameof(GreetingDialog)).ConfigureAwait(false);
. It reaches the BeginDialogAsync
method of my dialog and then continues to ContinueDialogAsync
. It works fine, however, while returning (using base.ContinueDialogAsync(outerDc, cancellationToken);
) from the method, I get some exception which I captured from the Diagnostic tool of the Visual Studio.
Also, I am sending the exception as an activity message to the client (bot framework emulator) which is shown below.
Sorry, it looks like something went wrong. Exception: System.ArgumentNullException: Value cannot be null. Parameter name: dialogId at Microsoft.Bot.Builder.Dialogs.DialogContext.BeginDialogAsync(String dialogId, Object options, CancellationToken cancellationToken) in D:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\DialogContext.cs:line 84 at Microsoft.Bot.Builder.Dialogs.ComponentDialog.BeginDialogAsync(DialogContext outerDc, Object options, CancellationToken cancellationToken) in D:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\ComponentDialog.cs:line 59 at Microsoft.Bot.Builder.Dialogs.DialogContext.BeginDialogAsync(String dialogId, Object options, CancellationToken cancellationToken) in D:\a\1\s\libraries\Microsoft.Bot.Builder.Dialogs\DialogContext.cs:line 84 at Chatbot.Bot.OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken) in C:\GIS\ChatbotNew\Chatbot\Chatbot\Bot.cs:line 120 at Microsoft.Bot.Builder.MiddlewareSet.ReceiveActivityWithStatusAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken) in D:\a\1\s\libraries\Microsoft.Bot.Builder\MiddlewareSet.cs:line 55 at Microsoft.Bot.Builder.BotAdapter.RunPipelineAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken) in D:\a\1\s\libraries\Microsoft.Bot.Builder\BotAdapter.cs:line 167
Update-1
Based on Drew's answer I removed outerDc.ContinueDialogAsync();
but I get some error (shown below) while stepping through the return base.BeginDialogAsync(outerDc, options, cancellationToken);
in BeginDialogAsync
function.
Also, while stepping through the OnTurnAsync
function I get an error while trying to get the BotState
as shown below.
TurnState
implementation:
public class TurnState
{
public int TurnCount { get; set; } = 0;
public string BotType { get; set; } = string.Empty;
public string ConversationLanguage { get; set; } = string.Empty;
//other properties...
}
Similar error while trying to create the DialogContext
as well.
ConfigureServices
in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddBot<Bot>(options =>
{
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
// Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection.
var botConfig = BotConfiguration.Load(@".\Chatbot.bot", secretKey);
services.AddSingleton(sp => botConfig);
// Retrieve current endpoint.
var service = botConfig.Services.Where(s => s.Type == "endpoint" && s.Name == "development").FirstOrDefault();
if (!(service is EndpointService endpointService))
{
throw new InvalidOperationException($"The .bot file does not contain a development endpoint.");
}
//options.CredentialProvider = new SimpleCredentialProvider(Configuration[MicrosoftAppCredentials.MicrosoftAppIdKey], Configuration[MicrosoftAppCredentials.MicrosoftAppPasswordKey]);
options.CredentialProvider = new SimpleCredentialProvider(endpointService.AppId, endpointService.AppPassword);
// Creates a logger for the application
ILogger logger = _loggerFactory.CreateLogger<Bot>();
// Catches any errors that occur during a conversation turn and logs them.
options.OnTurnError = async (context, exception) =>
{
await context.SendActivityAsync("Sorry, it looks like something went wrong. Exception: " + exception);
};
// 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();
var conversationState = new ConversationState(dataStore);
options.State.Add(conversationState);
});
// Create and register state accesssors.
// Acessors created here are passed into the IBot-derived class on every turn.
services.AddSingleton<BotAccessors>(sp =>
{
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.");
}
// Create the custom state accessor.
// State accessors enable other components to read and write individual properties of state.
var accessors = new BotAccessors(conversationState)
{
TurnStateAccessor = conversationState.CreateProperty<TurnState>(BotAccessors.TurnStateName),
};
return accessors;
});
}
Any help with this please?
Upvotes: 0
Views: 1121
Reputation: 33379
I'm trying to wrap my head around exactly what you're expecting to happen here:
public override Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
_turnState = _turnStateAccessor.GetAsync(outerDc.Context).Result;
outerDc.ContinueDialogAsync();
return base.BeginDialogAsync(outerDc, options, cancellationToken);
}
Specifically, why are you calling outerDc.ContinueDialogAsync()
here? That is basically tying the dialog stack up in a knot. If you removed that line everything else you show here should work perfectly.
If you update your question with some more details on exactly what you were trying to accomplish by calling that I'll happily update my answer to try and set you on the right path for accomplishing whatever that might be.
Upvotes: 0