2

I am needing some help. I am new at developing in Azure's Bot Framework, creating my first chatbot some weeks ago using a QnA KB. In any case, I managed to create the bot in Azure's portal and it is working perfectly. But I need to do some alterations and tests in Bot Framework Emulator (using V4) and, to do so, download and run locally my bot.

I made the download of my bot's .zip file following Azure's instructions and initialized it using Visual Studio 2017 as my IDE. When I hit the button "debug", though, the program tries to connect to localhost:3984.

using Autofac;
using System.Web.Http;
using System.Configuration;
using System.Reflection;
using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Internals;
using Microsoft.Bot.Connector;

namespace SimpleEchoBot
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            // Bot Storage: This is a great spot to register the private state storage for your bot. 
            // We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own!
            // For samples and documentation, see: https://github.com/Microsoft/BotBuilder-Azure

            Conversation.UpdateContainer(
                builder =>
                {
                    builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));

                    // Using Azure Table Storage

                    var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]); // requires Microsoft.BotBuilder.Azure Nuget package 

                    // To use CosmosDb or InMemory storage instead of the default table storage, uncomment the corresponding line below
                    // var store = new DocumentDbBotDataStore("cosmos db uri", "cosmos db key"); // requires Microsoft.BotBuilder.Azure Nuget package 
                    // var store = new InMemoryDataStore(); // volatile in-memory store

                    builder.Register(c => store)
                        .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
                        .AsSelf()
                        .SingleInstance();

                });
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
}

In matter of seconds, though, the following line of code returns the error System.ArgumentNullException: 'Value cannot be null. Arg_ParamName_Name':

var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]); // requires Microsoft.BotBuilder.Azure Nuget package 

As a consequence, I am unable to test my bot in the Bot Framework Emulator. Since I am really new with C#, could anyone give me a hand at solving this?

EDIT: Here is my web.config part of the code, just removing sensitive data:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=301879
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <appSettings>
    <!-- update these with your Microsoft App Id and your Microsoft App Password-->
    <!--<add key="BotId" value="" />-->
    <add key="MicrosoftAppId" value="" />
    <add key="MicrosoftAppPassword" value="" />
    <add key="QnaSubscriptionKey" value="" />
    <add key="QnaKnowledgebaseId" value="" />
    <add key="AzureWebJobsStorage" value="" />

  </appSettings>
  <!--
    For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.

    The following attributes can be set on the <httpRuntime> tag.
      <system.Web>
        <httpRuntime targetFramework="4.6" />
      </system.Web>
  -->
  <system.web>
    <customErrors mode="Off" />
    <compilation debug="true" targetFramework="4.6" />
    <httpRuntime targetFramework="4.6" />
  </system.web>
  <system.webServer>
    <defaultDocument>
      <files>
        <clear />
        <add value="default.htm" />
      </files>
    </defaultDocument>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Net.Http.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.29.0" newVersion="4.2.29.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.4.0" newVersion="5.1.4.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.IdentityModel.Protocol.Extensions" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.0.40306.1554" newVersion="1.0.40306.1554" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Bot.Connector" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.15.2.2" newVersion="3.15.2.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Bot.Builder" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.15.2.2" newVersion="3.15.2.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Azure.Documents.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.11.0.0" newVersion="1.11.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Bot.Builder.History" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.12.2.4" newVersion="3.12.2.4" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Bot.Builder.Autofac" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.15.2.2" newVersion="3.15.2.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.7.0.0" newVersion="5.7.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.7.0.0" newVersion="5.7.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-5.7.0.0" newVersion="5.7.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="mssqllocaldb" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>

EDIT 2: Some screenshots of the issue so far: enter image description here

enter image description here

EDIT 3: Trying to solve "Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at https://aka.ms/qnaabssetup.". Basic QnAMakerDialog.cs so far (the first instances of qnaAuthKey, qnaKBId and endpointHostName had values attributed to them, but I erased them at here for safety):

using System;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Bot.Builder.Azure;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.CognitiveServices.QnAMaker;
using Microsoft.Bot.Connector;
using System.Configuration;
namespace Microsoft.Bot.Sample.QnABot
{
    [Serializable]
    public class RootDialog : IDialog<object>
    {
        public async Task StartAsync(IDialogContext context)
        {
            /* Wait until the first message is received from the conversation and call MessageReceviedAsync 
            *  to process that message. */
            context.Wait(this.MessageReceivedAsync);
        }

        private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
        {
            /* When MessageReceivedAsync is called, it's passed an IAwaitable<IMessageActivity>. To get the message,
             *  await the result. */
            var message = await result;

            var qnaAuthKey = GetSetting("");
            var qnaKBId = ConfigurationManager.AppSettings[""];
            var endpointHostName = ConfigurationManager.AppSettings[""];

            // QnA Subscription Key and KnowledgeBase Id null verification
            if (!string.IsNullOrEmpty(qnaAuthKey) && !string.IsNullOrEmpty(qnaKBId))
            {
                // Forward to the appropriate Dialog based on whether the endpoint hostname is present
                if (string.IsNullOrEmpty(endpointHostName))
                    await context.Forward(new BasicQnAMakerPreviewDialog(), AfterAnswerAsync, message, CancellationToken.None);
                else
                    await context.Forward(new BasicQnAMakerDialog(), AfterAnswerAsync, message, CancellationToken.None);
            }
            else
            {
                await context.PostAsync("Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at https://aka.ms/qnaabssetup.");
            }

        }

        private async Task AfterAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
        {
            // wait for the next user message
            context.Wait(MessageReceivedAsync);
        }

        public static string GetSetting(string key)
        {
            var value = ConfigurationManager.AppSettings[key];
            if (String.IsNullOrEmpty(value) && key == "QnAAuthKey")
            {
                value = ConfigurationManager.AppSettings["QnASubscriptionKey"]; // QnASubscriptionKey for backward compatibility with QnAMaker (Preview)
            }
            return value;
        }
    }

    // Dialog for QnAMaker Preview service
    [Serializable]
    public class BasicQnAMakerPreviewDialog : QnAMakerDialog
    {
        // Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
        // Parameters to QnAMakerService are:
        // Required: subscriptionKey, knowledgebaseId, 
        // Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
        public BasicQnAMakerPreviewDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), ConfigurationManager.AppSettings["QnAKnowledgebaseId"], "No good match in FAQ.", 0.5)))
        { }
    }

    // Dialog for QnAMaker GA service
    [Serializable]
    public class BasicQnAMakerDialog : QnAMakerDialog
    {
        // Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
        // Parameters to QnAMakerService are:
        // Required: qnaAuthKey, knowledgebaseId, endpointHostName
        // Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
        public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), ConfigurationManager.AppSettings["QnAKnowledgebaseId"], "Desculpe, não entendi muito bem sua pergunta... Poderia refazê-la de forma diferente? Caso já tenha tentado, talvez eu ainda não saiba como responder sua dúvida.", 0.3, 1, ConfigurationManager.AppSettings["QnAEndpointHostName"])))
        { }

    }
}
3
  • Post in your web.config file after removing sensitive information. Commented May 14, 2018 at 19:42
  • Okay, I hope in the actual web.config file, the value of AzureWebJobsStorage is not null right? And also you haven't changed any code in the Global.asax, its the same as posted above right? Especially the line : var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]); Commented May 15, 2018 at 16:47
  • Don't worry, it's not null. In the global.asax, the only change I made was to input the value of AzureWebJobsStorage in the line var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]); Commented May 15, 2018 at 16:49

2 Answers 2

4

As the error mentions, the problem here is that, AzureWebJobsStorage cannot be found in the web.config file. To fix your problem, navigate to your bot in azure portal and click on app settings from the left blade. There you will find a setting with the key AzureWebJobsStorage. enter image description here Copy the value of the setting and paste that in the web.config file as :

<configuration>
  <appSettings>
    <!-- update these with your BotId, Microsoft App Id and your Microsoft App Password-->
    <add key="BotId" value="YourBotId" />
   <add key="MicrosoftAppId" value="" />
   <add key="MicrosoftAppPassword" value="" />
    <add key="QnaSubscriptionKey" value="" />
    <add key="QnaKnowledgebaseId" value="" />
    <add key="AzureWebJobsStorage" value="copied value from azure portal" />
  </appSettings>

Update:

To answer your further question posted in the comments,

I am redirected to a blank page in my browser, with the address localhost:3984 and a text where you can read QnABot Template, followed by some links

Indicates that the bot is working perfectly now and it is in fact the expected behavior. To test the bot locally, you need to use Bot Emulator. Follow the steps mentioned in the link and test the bot. Also you could go through the bot framework links, as that will give you more clarity on Microsoft Bot Framework.

Update 2:

As per your screenshots posted, I think you have changed the Global.asax file. The code for registering TableBotDataStore should be exactly

var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]); 

No change at all to this.

Secondly as mentioned by Fei Han :

The Utils.GetAppSetting(string key) method is for bots running on Azure, if you’d like to run your bot application on local and access settings from your web.config file, you can use ConfigurationManager.AppSettings[“your_key”] to replace Utils.GetAppSetting(“your_key”) in your QnAMakerDialog.

The second screenshots shows that you still have Utils.GetAppSetting(“your_key”) in your code. Replace it to :

var qnaKBId = ConfigurationManager.AppSettings["QnAKnowledgebaseId"];

and so on for other places as well.

Sign up to request clarification or add additional context in comments.

20 Comments

I did as you said and I believed it helped a bit. But when I tried to run the code, got the message System.FormatException: 'Settings must be of the form "name=value".', in the same part of the code...
Post in your web.config file content. Remove all sensitive information.
@PedroMartins updated the answer as per your screenshots
Okay, I believe it is almost solved. The only error appearing now is in Bot Framework Emulator, as POST 401 directline.postActivity. Any clues about it?
Okay, error 401 is gone (a stupid mistake of mine). Now, the emulator connects, but throws the message "Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at aka.ms/qnaabssetup." Believing this can be related to the basicQnAMakerDialog.cs, I decided to post how it currently is looking as an edition of the original post...
|
1

the error System.ArgumentNullException: 'Value cannot be null.

As Ashwin Kumar mentioned, you should include the name/value pairs for "AzureWebJobsStorage" in your web.config file for your bot application.

System.FormatException: 'Settings must be of the form "name=value".

Please check if you get the value ConfigurationManager.AppSettings["AzureWebJobsStorage"] and pass a valid connection string to initiate TableBotDataStore, not just pass the key "AzureWebJobsStorage".

enter image description here

I need to do some alterations and tests in Bot Framework Emulator (using V4) and, to do so, download and run locally my bot

The Utils.GetAppSetting(string key) method is for bots running on Azure, if you’d like to run your bot application on local and access settings from your web.config file, you can use ConfigurationManager.AppSettings[“your_key”] to replace Utils.GetAppSetting(“your_key”) in your QnAMakerDialog, like this:

//var qnaKBId = Utils.GetAppSetting("QnAKnowledgebaseId");
var qnaKBId = ConfigurationManager.AppSettings["QnAKnowledgebaseId"];

5 Comments

Hey man, thanks for the help. I believe I did what you asked, but still having some troubles. The program does not shoot an error anymore, but I am still unable to stablish a connection. Just posted as an edition of my question the contente of my web.config file, if this helps
@PedroMartins, so if you don't see any error, whats the problem you are facing? Can you elaborate your statement " unable to stablish a connection "
Of course. Once I execute the code, I am redirected to a blank page in my browser, with the address localhost:3984 and a text where you can read QnABot Template, followed by some links. And, just while I was writting this reply, I got the message System.ArgumentNullException: 'Valor não pode ser nulo. Arg_ParamName_Name' once again...
@PedroMartins, I had updated my answer based on your initial comment. Can you post in some additional screenshots of the issue, because this is not supposed to happen if you have followed exactly as mentioned in the answers.
Just posted some screenshots as requested, @Ashwim. Also, I most probably did some of the steps you described in here wrong, so thanks very much for keep helping.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.