Search code examples
c#azureasp.net-corebotframework

Access BotState variables from outside Bot class


In the bot classes we create ConversationState and UserState objects that we can access using its accessors and create properties on them, and then save the data we store.

But how could I do the same if I want to access the data from a Dialog that's called from the Bot class? I know I can pass an object through the Context using BeginDialogAsync options parameter. How could I pass two of them instead of just one and how to get them in the dialog class?

Is there a way to access ConversationState and UserState without having to pass them from dialog to dialog?

public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
    await base.OnTurnAsync(turnContext, cancellationToken);

    // Save any state changes that might have occured during the turn.
    await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    Logger.LogInformation("Running dialog with Message Activity.");

    // Run the Dialog with the new message Activity.
    await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
}

In this function of the CoreBot sample we can see the ConversationState and UserState are saved but they are not modified anywhere else, and in the second function a DialogState property is created in the child dialog but it isn't used either that I can see? Can somebody explain why are they created and how to access them from inside the Dialog that is just called?


Solution

  • You can use dependency injection.

     public class UserStateClass
     {
            public string name { get; set; }
     }
    

    and

     public class YourDialog : ComponentDialog
     {
            private readonly IStatePropertyAccessor<UserStateClass> _userStateclassAccessor;
    
            public YourDialog(UserState userState)
                : base(nameof(YourDialog))
            {
                _userProfileAccessor = userState.CreateProperty<UserStateClass>("UserProfile");
    
                WaterfallStep[] waterfallSteps = new WaterfallStep[]
                {
                    FirstStepAsync,
                };
                AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
            }
    
            private async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
            {
                var userstate = await _userStateclassAccessor.GetAsync(stepContext.Context, () => new UserStateClass(), cancellationToken);
    
                userstate.name = "pepe";
                return await stepContext.EndDialogAsync();
            }
     }