如何使用 directLine 通道在 Azure Table 存储中存储和检索机器人数据?
How to store & retrieve Bot Data in Azure Table storage with directLine channel?
我正在使用 Microsoft Bot Framework
和 directLine
频道。我的机器人是公司客户门户的一部分,我从那里获取一些用户信息并使用 stateClient
将其存储在 BotState
中,如下所示
public ActionResult Index()
{
var userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
GetTokenViaBootStrap().Wait();
var botCred = new MicrosoftAppCredentials(
ConfigurationManager.AppSettings["MicrosoftAppId"],
ConfigurationManager.AppSettings["MicrosoftAppPassword"]);
var stateClient = new StateClient(botCred);
BotState botState = new BotState(stateClient);
BotData botData = new BotData(eTag: "*");
botData.SetProperty<string>("UserName", result.UserInfo.GivenName + " " + result.UserInfo.FamilyName);
botData.SetProperty<string>("Email", result.UserInfo.DisplayableId);
botData.SetProperty<string>("GraphAccessToken", UserAccessToken);
botData.SetProperty<string>("TokenExpiryTime", result.ExpiresOn.ToString());
stateClient.BotState.SetUserDataAsync("directline", userId, botData).Wait();
var UserData = new UserInformationModel
{
UserId = userId,
UserName = result.UserInfo.GivenName + " " + result.UserInfo.FamilyName
};
return View(UserData);
}
作为 directLine
频道,我使用 javascript 中的秘密连接我的机器人,如下所示:
BotChat.App({
bot: { id: 'my_bot_id', name: 'my_bot_id' },
resize: 'detect',
sendTyping: true, // defaults to false. set to true to send 'typing' activities to bot (and other users) when user is typing
user: { id: UserData.UserId},
directLine: {
secret: "my_bot_secret"
}
}, document.getElementById('my_bot_id'));
我正在访问在 MVC 站点中捕获的 Node js Bot 中的用户信息数据,如下所示:
function sessionUserCapture(session) {
switch (session.message.address.channelId) {
case 'skypeforbusiness':
// some code
break;
case 'directline':
userName= session.userData.UserName;
userEmail= session.userData.Email;
//some code
break;
case 'slack':
// some code
}
}
我参考了 Microsoft 的 Save state data from Manage state data 用于上述代码,然后我使用 session
中可用的 userData
在我的 Node.JS
Bot 中访问此数据。
由于 StateClient 已弃用,我参考 this 将 stateclient
替换为 Azure Table storage
。但是,我无法理解如何将上述数据存储在 Table Storage
中。
任何人都可以推荐任何我可以参考的文章来解决这个问题吗?
我的机器人在 NodeJs
中,我正在 C# MVC application
中使用 directLine
频道。
您正在使用的代码正在使用 已弃用 默认状态,将无法运行。为了完成您想要的,这取决于您在代码中的位置。决定因素是您是否有权访问 context
对象。
例如,如果您在 MessagesController
中,您将无权访问上下文对象,您的代码可能如下所示:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
var message = activity as IMessageActivity;
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = Address.FromActivity(message);
var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);
userData.SetProperty("key 1", "value1");
userData.SetProperty("key 2", "value2");
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
await botDataStore.FlushAsync(key, CancellationToken.None);
}
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
}
然后获取数据:
userData.GetProperty<string>("key 1");
另一种情况是,如果您确实可以访问上下文对象,例如 Dialog
,您的代码可能如下所示:
context.UserData.SetValue("key 1", "value1");
context.UserData.SetValue("key 2", "value2");
然后获取数据:
context.UserData.GetValue<string>("key 1");
您是否将机器人配置为连接到 Global.asax.cs 中的 Azure Table 存储,而不是弃用的默认状态?
我写了一篇博客 post,详细介绍了如何将机器人的状态移动到 Azure Table 存储,您应该查看一下
here
protected void Application_Start()
{
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
// Using Azure Table for storage
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
GlobalConfiguration.Configure(WebApiConfig.Register);
}
一种选择是使用适用于 .NET 的 Microsoft Azure 存储客户端库,如此处的答案中所述: Just make sure to follow the exact same PartitionKey strategy as is followed by the TableBotDataStore class,然后序列化数据字段正确。
--
编辑:
我对此进行了测试,它确实按预期工作。
public class WebChatController : Controller
{
public ActionResult Index()
{
var connectionString = ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("BotStore");
string userId = Guid.NewGuid().ToString();
TableQuery<BotDataRow> query = new TableQuery<BotDataRow>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, userId));
var dataRow = table.ExecuteQuery(query).FirstOrDefault();
if(dataRow != null)
{
dataRow.Data = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
UserName = "This user's name",
Email = "whatever@email.com",
GraphAccessToken = "token",
TokenExpiryTime = DateTime.Now.AddHours(1)
});
dataRow.Timestamp = DateTimeOffset.UtcNow;
table.Execute(TableOperation.Replace(dataRow));
}
else
{
var row = new BotDataRow(userId, "userData");
row.Data = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
UserName = "This user's name",
Email = "whatever@email.com",
GraphAccessToken = "token",
TokenExpiryTime = DateTime.Now.AddHours(1)
});
row.Timestamp = DateTimeOffset.UtcNow;
table.Execute(TableOperation.Insert(row));
}
var vm = new WebChatModel();
vm.UserId = userId;
return View(vm);
}
public class BotDataRow : TableEntity
{
public BotDataRow(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public BotDataRow() { }
public bool IsCompressed { get; set; }
public string Data { get; set; }
}
}
在节点机器人中:
'use strict';
const builder = require('botbuilder');
const restify = require('restify');
var azure = require('botbuilder-azure');
var tableName = 'BotStore';
var azureTableClient = new azure.AzureTableClient(tableName,'accountname','accountkey');
var tableStorage = new azure.AzureBotStorage({ gzipData: false }, azureTableClient);
const connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3979, () => {
console.log(`${server.name} listening to ${server.url}`);
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector)
.set('storage', tableStorage);;
bot.dialog('/',
[
function (session){
var data = session.userData;
}
]);
我正在使用 Microsoft Bot Framework
和 directLine
频道。我的机器人是公司客户门户的一部分,我从那里获取一些用户信息并使用 stateClient
将其存储在 BotState
中,如下所示
public ActionResult Index()
{
var userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
GetTokenViaBootStrap().Wait();
var botCred = new MicrosoftAppCredentials(
ConfigurationManager.AppSettings["MicrosoftAppId"],
ConfigurationManager.AppSettings["MicrosoftAppPassword"]);
var stateClient = new StateClient(botCred);
BotState botState = new BotState(stateClient);
BotData botData = new BotData(eTag: "*");
botData.SetProperty<string>("UserName", result.UserInfo.GivenName + " " + result.UserInfo.FamilyName);
botData.SetProperty<string>("Email", result.UserInfo.DisplayableId);
botData.SetProperty<string>("GraphAccessToken", UserAccessToken);
botData.SetProperty<string>("TokenExpiryTime", result.ExpiresOn.ToString());
stateClient.BotState.SetUserDataAsync("directline", userId, botData).Wait();
var UserData = new UserInformationModel
{
UserId = userId,
UserName = result.UserInfo.GivenName + " " + result.UserInfo.FamilyName
};
return View(UserData);
}
作为 directLine
频道,我使用 javascript 中的秘密连接我的机器人,如下所示:
BotChat.App({
bot: { id: 'my_bot_id', name: 'my_bot_id' },
resize: 'detect',
sendTyping: true, // defaults to false. set to true to send 'typing' activities to bot (and other users) when user is typing
user: { id: UserData.UserId},
directLine: {
secret: "my_bot_secret"
}
}, document.getElementById('my_bot_id'));
我正在访问在 MVC 站点中捕获的 Node js Bot 中的用户信息数据,如下所示:
function sessionUserCapture(session) {
switch (session.message.address.channelId) {
case 'skypeforbusiness':
// some code
break;
case 'directline':
userName= session.userData.UserName;
userEmail= session.userData.Email;
//some code
break;
case 'slack':
// some code
}
}
我参考了 Microsoft 的 Save state data from Manage state data 用于上述代码,然后我使用 session
中可用的 userData
在我的 Node.JS
Bot 中访问此数据。
由于 StateClient 已弃用,我参考 this 将 stateclient
替换为 Azure Table storage
。但是,我无法理解如何将上述数据存储在 Table Storage
中。
任何人都可以推荐任何我可以参考的文章来解决这个问题吗?
我的机器人在 NodeJs
中,我正在 C# MVC application
中使用 directLine
频道。
您正在使用的代码正在使用 已弃用 默认状态,将无法运行。为了完成您想要的,这取决于您在代码中的位置。决定因素是您是否有权访问 context
对象。
例如,如果您在 MessagesController
中,您将无权访问上下文对象,您的代码可能如下所示:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
var message = activity as IMessageActivity;
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = Address.FromActivity(message);
var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);
userData.SetProperty("key 1", "value1");
userData.SetProperty("key 2", "value2");
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
await botDataStore.FlushAsync(key, CancellationToken.None);
}
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
}
然后获取数据:
userData.GetProperty<string>("key 1");
另一种情况是,如果您确实可以访问上下文对象,例如 Dialog
,您的代码可能如下所示:
context.UserData.SetValue("key 1", "value1");
context.UserData.SetValue("key 2", "value2");
然后获取数据:
context.UserData.GetValue<string>("key 1");
您是否将机器人配置为连接到 Global.asax.cs 中的 Azure Table 存储,而不是弃用的默认状态?
我写了一篇博客 post,详细介绍了如何将机器人的状态移动到 Azure Table 存储,您应该查看一下
here
protected void Application_Start()
{
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
// Using Azure Table for storage
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
GlobalConfiguration.Configure(WebApiConfig.Register);
}
一种选择是使用适用于 .NET 的 Microsoft Azure 存储客户端库,如此处的答案中所述:
-- 编辑: 我对此进行了测试,它确实按预期工作。
public class WebChatController : Controller
{
public ActionResult Index()
{
var connectionString = ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("BotStore");
string userId = Guid.NewGuid().ToString();
TableQuery<BotDataRow> query = new TableQuery<BotDataRow>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, userId));
var dataRow = table.ExecuteQuery(query).FirstOrDefault();
if(dataRow != null)
{
dataRow.Data = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
UserName = "This user's name",
Email = "whatever@email.com",
GraphAccessToken = "token",
TokenExpiryTime = DateTime.Now.AddHours(1)
});
dataRow.Timestamp = DateTimeOffset.UtcNow;
table.Execute(TableOperation.Replace(dataRow));
}
else
{
var row = new BotDataRow(userId, "userData");
row.Data = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
UserName = "This user's name",
Email = "whatever@email.com",
GraphAccessToken = "token",
TokenExpiryTime = DateTime.Now.AddHours(1)
});
row.Timestamp = DateTimeOffset.UtcNow;
table.Execute(TableOperation.Insert(row));
}
var vm = new WebChatModel();
vm.UserId = userId;
return View(vm);
}
public class BotDataRow : TableEntity
{
public BotDataRow(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public BotDataRow() { }
public bool IsCompressed { get; set; }
public string Data { get; set; }
}
}
在节点机器人中:
'use strict';
const builder = require('botbuilder');
const restify = require('restify');
var azure = require('botbuilder-azure');
var tableName = 'BotStore';
var azureTableClient = new azure.AzureTableClient(tableName,'accountname','accountkey');
var tableStorage = new azure.AzureBotStorage({ gzipData: false }, azureTableClient);
const connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3979, () => {
console.log(`${server.name} listening to ${server.url}`);
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector)
.set('storage', tableStorage);;
bot.dialog('/',
[
function (session){
var data = session.userData;
}
]);