Search code examples
c#azurebotframework

Migration of BotState to Azure Table Storage: no more possibility to access UserData without IDialogContext?


I recently switched my custom state data from the now deprecated StateClient to Azure Table Storage using Microsoft documentation, following the steps:

  • Created a storage
  • Added to connection strings
  • Added to Autofac registration in Global.asax.cs

This is working well for all call like context.UserData.SetValue("myKey", "myValue"); inside dialogs.

But it seems that there is no more possibility to get UserData directly from an Activity object when we have no IDialogContext, for example if you want to use these data from your MessageController.

Previously, I was doing:

var botState = activity.GetStateClient().BotState;
var userData = await botState.GetUserDataAsync(activity.ChannelId, activity.From.Id, token);

These actions are announced as deprecated in latest BotBuilder release. Any solution to do the equivalent?

EDIT:

Adding my global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configuration.Filters.Add(new ExceptionFilter());

        var builder = new ContainerBuilder();

        //register the bot builder module
        builder.RegisterModule(new DialogModule());

        //register project dependencies
        builder.RegisterModule(new BotModule());

        //Http config 
        var config = GlobalConfiguration.Configuration;

        //register controller
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        //create container
        var container = builder.Build();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

        GlobalConfiguration.Configure(WebApiConfig.Register);
    }
}

And the BotModule associated:

public class BotModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        base.Load(builder);

        // Scorable pour l'interruption de dialogue
        builder.Register(c => new InterruptScorable(c.Resolve<IDialogTask>())).As<IScorable<IActivity, double>>().InstancePerLifetimeScope();
        
        // Manage BotData in Sql Datatable
        var store = new TableBotDataStore(ConfigurationHelper.XXXXX, "BotData");
        builder.Register(c => store).Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore).AsSelf().SingleInstance();
        builder.Register(c => new CachingBotDataStore(store, CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency)).As<IBotDataStore<BotData>>().AsSelf().InstancePerLifetimeScope();

        // Logger de conversation
        builder.Register(c => new ActivityLogger(c.Resolve<IBotData>())).As<IActivityLogger>().InstancePerLifetimeScope();

        // Dialogue de base
        builder.RegisterType<RootDialog>().As<IDialog<object>>().InstancePerDependency();
    }
}

Solution

  • I've modified your code a little, to remove the deprecated .Build() method.

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            Conversation.UpdateContainer(builder =>
            {
                builder.RegisterModule(new DialogModule());
                builder.RegisterModule<BotModule>();
                builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            });
    
            GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(Conversation.Container);
    
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
    

    After registering the modules this way, the code Jason Sowers shared on this issue should help: How can I access Bot Framework ConversationData outside of a dialog?