1

I have built a bot which can handle two languages(English and German). When user talks to the bot for the first time, it asks the user about language preference and stores it (Rest of the conversation happens in selected language). I am storing that preference in Bot State but it is giving me some concurrency issues. For example if other user changes the language, it gets changed for everyone. How can I avoid this? I am doing following in the root dialog:

context.UserData.SetValue<string>("Language", "de-DE");
Globals.Locale = "de-DE";

And following is Globals class:

public class Globals
{
    private static string _locale;
    public static string Locale
    {
        get
        {
            return _locale;
        }
        set
        {
            _locale = value;
        }
    }
}

And following is how i am setting Culture

Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(Globals.Locale);
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(Globals.Locale);

The reason I am using global variable is that i set the culture outside the dialog and i don't have context there. Is there any better way to do it. Thanks

Ehsan Ul Haq
  • 209
  • 3
  • 12
  • Take a look here: https://stackoverflow.com/a/2583490/1161132. You need to do something similar. Answer is not specific to your question so i'm not posting as an 'answer' but same idea. Keep a single instance of each locale you need. Each "UserData" has an assigned local rather than using a global one. The linked example shows usage with a double but you would need to do same with a string. – bgura Feb 08 '18 at 16:15

2 Answers2

1

You can set the activity.Locale = context.UserData.SetValue<string>("Language") before calling await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); then there is no need to set the thread's culture (the sdk will do this for you based on activity.Locale: see LocalizedScope )

There's an example of this here: https://github.com/EricDahlvang/ChooseLanguageBot

The code in the messages controller is as follows:

if (activity.Type == ActivityTypes.Message)
{
    using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
    {
        var botData = scope.Resolve<IBotData>();
        await botData.LoadAsync(CancellationToken.None);

        var lcid = botData.PrivateConversationData.GetValueOrDefault<string>("LCID");
        if (!string.IsNullOrEmpty(lcid))                    
            activity.Locale = lcid;

        await botData.FlushAsync(CancellationToken.None);
    }

    await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}

This example is setting a value in botData.PrivateConversationData but you could also use botData.UserData.

Eric Dahlvang
  • 8,252
  • 4
  • 29
  • 50
  • I added above piece of code in message controller and it throws ''Microsoft.Rest.HttpOperationException' in mscorlib.dll' exception. Reason : The data is changed. Code: 412 How can i avoid this? – Ehsan Ul Haq Feb 10 '18 at 11:02
1

The solution Eric presented works but it throws an Exception ''Microsoft.Rest.HttpOperationException' in mscorlib.dll'' which can be handled by adding following code in Globals.asax file

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);
        BotConfig.UpdateConversationContainer();

    }
}

public static class BotConfig
{
    public static void UpdateConversationContainer()
    {
        var builder = new ContainerBuilder();

        builder
            .Register(c => new CachingBotDataStore(c.ResolveKeyed<IBotDataStore<BotData>>(typeof(ConnectorStore)), CachingBotDataStoreConsistencyPolicy.LastWriteWins))
            .As<IBotDataStore<BotData>>()
            .AsSelf()
            .InstancePerLifetimeScope();
        builder.Update(Conversation.Container);
    }
}
Ehsan Ul Haq
  • 209
  • 3
  • 12