Reputation: 47
I've been implementing the following for a multi-tenant azure bot in C# and this works great for authenticating the app credentials.
C# : Creating a Single Bot Service to Support Multiple Bot Applications
However, I am wondering if the same is possible for the bot data store as well? I currently have the standard Azure Table storage bot data store implemented like so which uses a single connection string defined in the web.config:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new ReflectionSurrogateModule());
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
//var store = new InMemoryDataStore();
var store = new TableBotDataStore(ConfigurationManager.AppSettings["BotStorage"]);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
In my ICredentialProvider implementation (as outlined in the articles examples). If I store the customers connection string along side their App ID and Password, is it possible to pull that reference in the dependency injection above based on the customer/bot serialising the data store and then save/read that data to the customers Azure table storage?
Upvotes: 1
Views: 283
Reputation: 420
You would need to create a custom bot data store that implements IBotDataStore<BotData>
and use that. I was able to get a simple one that created the same TableBotDataStore
each time. Below is what I used and a break point in the constructor gets hit each time I send a message.
public class CustomBotStore : IBotDataStore<BotData>
{
private IBotDataStore<BotData> _store;
public CustomBotStore()
{
string connString = ConfigurationManager.ConnectionStrings["BotState"].ConnectionString;
_store = new TableBotDataStore(connString, "testbotdata"); // requires Microsoft.BotBuilder.Azure Nuget package
}
public Task<bool> FlushAsync(IAddress key, CancellationToken cancellationToken)
{
return _store.FlushAsync(key, cancellationToken);
}
public Task<BotData> LoadAsync(IAddress key, BotStoreType botStoreType, CancellationToken cancellationToken)
{
return _store.LoadAsync(key, botStoreType, cancellationToken);
}
public Task SaveAsync(IAddress key, BotStoreType botStoreType, BotData data, CancellationToken cancellationToken)
{
return _store.SaveAsync(key, botStoreType, data, cancellationToken);
}
}
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
builder.Register(c => new CustomBotStore())
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.InstancePerLifetimeScope();
});
}
}
I'm not sure what the implications of having an instance per lifetime scope are because the sample uses a single instance. But you should be able to use this and then inspect the HttpContext
in the constructor and use the correct connection string.
Upvotes: 1