-
Notifications
You must be signed in to change notification settings - Fork 479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bot state so that the context service key is used consistently to access the cached state #3727
Conversation
libraries/Microsoft.Bot.Builder.Dialogs/Memory/Scopes/BotStateMemoryScope.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🕐
OK, here's what I think we should do:
This gets rid of all reflection, and should be correct. |
- Make BotState.CachedBotState public while changing its members from public to internal - Make BotState.GetCachedState public - Add a null check for newly public method and use BotAssert in other methods for consistency - Document newly public method - Write test for newly public method
@tomlm - I've implemented your solution, and all checks are passing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good, thanks for the changess |
Fixes #3643
A bot state class's "context service key" is used to access the cached bot state in the turn state collection. Because the context service key is usually the name of the type (e.g. "UserState") there were some cases where the SDK was just using the type name instead of the actual context service key. This fails when a bot state class has a context service key that's different from the name of the type, as my new tests show.
In the case of a bot state memory scope, I also fixed the code to not rely on reflection to access the cached bot state.BotStateMemoryScope<T>.GetMemory
now accesses the bot state instance in the same way thatBotStateMemoryScope<T>.LoadAsync
andBotStateMemoryScope<T>.SaveChangesAsync
do, and once it has the bot state instance it can simply callBotState.Get
. Because the classes that callGetMemory
are expecting a dictionary and not a JObject, I modifiedBotState.Get
to return anIDictionary<string, object>
. I believe this is an improvement because that's the form that the cached bot state is already in and there's no need to convert it to a JObject. While it may seem like a dangerous change, the only place thatBotState.Get
is used is in one InspectionMiddleware method, and if any customers are using it and expecting a JObject then the compiler will just tell them to convert it to a JObject on their end, so there won't be any surprising behavior changes. I'm confident that the code is better this way and I can't imagine this breaking anything because the tests are all passing.I have discovered that the problems I was seeing in
BotStateMemoryScope<T>
were not aboutGetMemory
returning a JObject instead of a dictionary, they were aboutBotState.Get
returning a copy of the of the state, which means any modifications wouldn't persist. I have also discovered that my "best option" to fixBotStateMemoryScope<T>
by havingGet
return the original dictionary instead of a JObject will not work because it violates the contract. I see four other options:Get
method (with a different name) to return the actual dictionary instead of a copyGet
method and have the Bot Builder assembly share its members with the dialogs libraryGet
method and haveBotStateMemoryScope<T>.GetMemory
access it using reflectionBotStateMemoryScope<T>.GetMemory
use reflection twiceI went with option 4 because it's the most similar to the way things are currently set up. The reason we need to use reflection twice is because the
CachedBotState
class is private and the context service key we need to access it is also private.EDIT: I've thought of a fifth option. Since bot state is meant to be accessed using bot state property accessors, we could try to rework the adaptive dialogs library to access bot state using bot state property accessors. This would probably represent significant work/changes so I would not attempt it without some kind of go-ahead from the SDK team.