问题
I am migrating from the old botframework v4 to the latest version. I have around 50 dialog classes and it is so much work to inject the userstate dependency on all of them.
Before i can access the model by just this code:
var userstate = await (stepContext.Context.TurnState["BasicAccessors"] as BasicAccessors).BasicUserStateAccessor.GetAsync(stepContext.Context);
and no need to inject the userState in every dialog. I tried doing this but got 500 error and other ones. How can i replicate this method to the latest version?
This is the code from the old version.
BasicAccessors class:
public class BasicAccessors
{
public BasicAccessors(ConversationState conversationState, UserState userState)
{
ConversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
UserState = userState ?? throw new ArgumentException(nameof(userState));
}
public static string DialogStateAccessorName { get; } = $"{nameof(BasicAccessors)}.DialogState";
public static string BasicUserStateAccessorName { get; } = $"{nameof(BasicAccessors)}.BasicUserState";
public IStatePropertyAccessor<BasicUserState> BasicUserStateAccessor { get; internal set; }
public IStatePropertyAccessor<DialogState> DialogStateAccessor { get; internal set; }
public ConversationState ConversationState { get; }
public UserState UserState { get; }
}
OnTurnAsync:
turnContext.TurnState.Add("BasicAccessors", _basicAccessors);
StartUp:
services.AddSingleton<BasicAccessors>(sp =>
{
var options = sp.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
var conversationState = options.State.OfType<ConversationState>().FirstOrDefault();
var userState = options.State.OfType<UserState>().FirstOrDefault();
var accessors = new BasicAccessors(conversationState, userState)
{
DialogStateAccessor = conversationState.CreateProperty<DialogState>(BasicAccessors.DialogStateAccessorName),
BasicUserStateAccessor = userState.CreateProperty<BasicUserState>(BasicAccessors.BasicUserStateAccessorName),
};
return accessors;
});
回答1:
I actually just answered this in GitHub a couple days ago.
You already have an -accessors
class. You just need to change it to something more like this (adjust to match your code):
using Microsoft.Bot.Builder;
using Microsoft.BotBuilderSamples;
namespace Microsoft.BotBuilderSamples
{
public class BasicAccessors
{
public IStatePropertyAccessor<ConversationData> ConversationStateAccessors { get; }
public IStatePropertyAccessor<UserProfile> UserStateAccessors { get; }
public StateAccessors(ConversationState conversationState, UserState userState)
{
ConversationStateAccessors = conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
UserStateAccessors = userState.CreateProperty<UserProfile>(nameof(UserProfile));
}
}
}
Notice how the properties each get set with the actual accessor via *State.CreateProperty<*>(nameof(*));
Then, in Startup.cs
, you just need:
services.AddSingleton<StateAccessors>();
services.AddSingleton<MyDialogThatNeedsAccessors>(); // Call this for each of your Dialogs
Then, in your dialogs that need the accessors,
public MyDialogThatNeedsAccessors(BasicAccessors stateAccessors)
{
_userProfileAccessor = stateAccessors.UserStateAccessors;
}
Then, to access it within your dialog, you just do something like:
var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile());
For dialogs that don't need the accessors, just leave it out of the constructor.
来源:https://stackoverflow.com/questions/57578289/accessing-model-database-without-injecting-the-userstate-to-every-dialog