craigbot
craigbot

Reputation: 355

Passing Accessors into WaterfallDialog causes error after first step Bot Framework v4

I'm having an issue with BotAccessors when I pass it from the main dialog into another dialog. I'm motivated to send accessors up because I need to access and then save state within a UserProfile from any dialog. This is how I'm passing it:

await dialogContext.BeginDialogAsync(SiteReviewDialog.id, _accessors, cancellationToken: cancellationToken);

The SiteReviewDialog runs its first step fine and prompts the user, but it errors before even getting to the second step (breakpoints do not get hit). I tried removing all references to accessors in the SiteReviewDialog, and it seems the only way to fix this is to not pass accessors at all. If I pass accessors the error I get is: http://freetexthost.com/sdgdilpyxv

From the error, on line 232 is within my OnTurnAsync:

var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);

I tried to follow the code execution in the SiteReviewDialog from the first AddStep(async (stepContext, cancellationToken) and it exits out line by line then hits OnTurnAsync and gets to line 232 then errors. Does anyone have any suggestions?

Upvotes: 1

Views: 160

Answers (1)

Drew Marsh
Drew Marsh

Reputation: 33379

To explain what's going on first: the parameter you're passing the _accessors to is reserved for an "options" object for the Dialog. This would be something like settings for how the Dialog should behave. For example, in the case of prompts this is always a PromptOptions subclass. These options objects get serialized and stored on the stack for the execution lifetime of the dialog, so the reason you're getting an exception passing your _accessors is because it contains types that are not serializable. So, long story short, you don't want to pass your accessors this way.

Accessors are effectively singletons and what you would want to do is pass them through the constructor chain of your dialogs. So you would take IStatePropertyAccessor<T> (or an "accessors" class if you've decided to follow that pattern) to your SiteReviewDialog constructor and then make sure that passes it on to any child dialogs it creates via their constructors. NOTE: dialogs themselves should really be created as singletons as well.

From there it's just a matter of using the accessor when you need it because you always give it a ITurnContext from which it should be loading its data and the singleton instance uses identifiers specific to that context to ensure the correct data is loaded.

Upvotes: 1

Related Questions