Accessing model/database without injecting the UserState to every dialog

为君一笑 提交于 2019-12-13 20:21:19

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!