Search code examples
c#azurebotframework

How to save and later retrieve data acquired from the OnEventActivityAsync method


I am making a bot in the Azure Bot Framework that is built to filter the answers it can give differently depending on what page(of a website) the user is on. To get the data, I am posting an event activity to my bot via the back-channel method which is being caught and handled. The problem is that I need to be able to store its data in some type of storage (Like the conversationState for example), and then retrieve it later when an OnMessageActivityAsync is triggered, as that is what sends that data to be filtered. The jump between getting the data in OnEventActivityAsync and using it OnMessageActivityAsync is where I am failing.

I have tried using variables like temp that are stored in the DialogBot constructor, but they never get changed by the OnEventActivityAsync method, so temp stays as "", never being changed to "king." Also, I am 100% certain that it is getting into the OnEventActivityAsync method, as I tested it before with testing code (that was taken out here for less confusion.)

I have also tried using the conversationState, but the problem I ran into here is that whenever I tried to use the _conversationState.GetPropertyValueAsync<>() or _conversationState.SetPropertyValueAsync<>() method it would say that the method was inaccessible due to its protection level. So, while there were methods to get and set the property values, I am unable to use them for some reason.

I also tried the overriding this method, and making a derived class but neither worked.

--EDIT-- DialogBot.cs --> (The Base)

public class DialogBot<T> : ActivityHandler where T : Dialog
    {
        protected readonly Dialog _dialog;
        protected readonly BotState _conversationState;
        protected readonly BotState _userState;
        protected readonly ILogger _logger;
        IStatePropertyAccessor<QnAPrompting.Helpers.FilterHolder> convoAccess;
        //FilterHolder convoData;

        String temp;

        public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
        {
            _conversationState = conversationState;
            _userState = userState;
            _dialog = dialog;
            _logger = logger;
            temp = "";
            convoAccess = _conversationState.CreateProperty<FilterHolder>("metaNV");
        }

        public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken 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);
        }

OnEventActivityAsync:

        protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
        {
            string[] paths = { ".", "Helpers", "a.json" };
            string fullPath = Path.Combine(paths);
            System.IO.File.WriteAllText(fullPath, "-->The Filter from the event is: " + turnContext.Activity.Name + "\n");


            var settheFilter = await convoAccess.GetAsync(turnContext, () => new FilterHolder());
            settheFilter.filter = turnContext.Activity.Name;
            await _conversationState.SaveChangesAsync(turnContext);
        }

OnMessageActivityAsync:

            _logger.LogInformation("Running dialog with Message Activity."); 

            string[] paths = { ".", "Helpers", "a.json" };
            string fullPath = Path.Combine(paths);
            System.IO.File.AppendAllText(fullPath, "-->In OnMessage\n");


            var settheFilter = await convoAccess.GetAsync(turnContext, () => new FilterHolder());
            System.IO.File.AppendAllText(fullPath, "-->The filter is: " + settheFilter.filter + "\n");

            turnContext.Activity.Summary = settheFilter.filter;

            System.IO.File.AppendAllText(fullPath, "-->The Summary is: " + turnContext.Activity.Summary + "\n");
            //--------------------------------
            await _dialog.Run(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);  //meta filter
            //--------------------------------

Expected Result:

–>The Filter from the event is: queen-queenvalue
–>In OnMessage
–>The filter is: queen-queenvalue
–>The Summary is: queen-queenvalue

Actual Result:

–>The Filter from the event is: queen-queenvalue
–>In OnMessage
–>The filter is: king-kingvalue
–>The Summary is: king-kingvalue

So while it is saving the metaNV property initially, if you switch to a different page it does not.


Solution

  • Have at look at the documentation for accessing state:
    https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-concept-state
    https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-state

    Like you said, SetPropertyValueAsync and GetPropertyValueAsync are protected so you're not supposed to use those. Instead, you are supposed to call SetAsync and GetAsync on the property accessor objects. What you're supposed to do with a bot state object (like a ConversationState object for example) is call CreateProperty to create state accessors, and call SaveChangesAsync.

    And you're never supposed to store anything in a local variable that lasts for more than one turn. As the documentation explains, a totally different instance of your bot could be responding to the user on the next turn. Code your bot as though you expect the program to shut down and restart between each turn, so that on each turn its local memory is wiped clean.

    You can refer to some of the samples like the Welcome User sample to see how state property accessors are supposed to work:

    var welcomeUserStateAccessor = _userState.CreateProperty<WelcomeUserState>(nameof(WelcomeUserState));
    var didBotWelcomeUser = await welcomeUserStateAccessor.GetAsync(turnContext, () => new WelcomeUserState());