我有几个机器人访问CosmosDB。他们通过客户端的URL参数获取userId。
在同一时间在不同实例中使用相同的bot时出现我的问题。
假设我开始与用户ID“1”进行对话,输入一些数据,如姓名,年龄,性别。在对话过程中,我打开另一个用户ID为“2”的网站。用户2输入名称。用户1完成配置文件创建。提示用户2年龄。突然,来自用户2的用户ID变为“1”,并且配置文件数据与来自用户1的用户数据相同。
我不时会收到以下错误:
{ code: 412,
body: ‘{“code”:“PreconditionFailed”,“message”:“Operation cannot be performed because one of the specified precondition is not met., \r\nRequestStartTime: 2019-03-07T20:57:19.6998897Z, RequestEndTime: 2019-03-07T20:57:19.7098745Z, Number of regions attempted: 1\r\nResponseTime: 2019-03-07T20:57:19.7098745Z, StoreResult: StorePhysicalAddress: rntbd://cdb-ms-prod-westeurope1-fd21.documents.azure.com:16817/apps/9bc5d0cc-9b7c-4b1d-9be2-0fa2654271c4/services/3507a423-5215-48ca-995a-8763ec527db8/partitions/940cd512-aa34-4c93-9596-743de41037da/replicas/131964420031154286p/, LSN: 172, GlobalCommittedLsn: 171, PartitionKeyRangeId: 0, IsValid: True, StatusCode: 412, SubStatusCode: 0, RequestCharge: 1.24, ItemLSN: -1, SessionToken: 172, UsingLocalLSN: False, TransportException: null, ResourceType: Document, OperationType: Replace\r\n, Microsoft.Azure.Documents.Common/2.2.0.0”}’,
activityId: ‘fbdaeedb-a73f-4052-bd55-b1417b10583f’ }
因此,机器人似乎以某种方式混淆了用户ID。
我将UserState和ConversationState保存在本地存储中,因为我不需要将它们保存在我的数据库中:
// Local browser storage
const memoryStorageLocal = new MemoryStorage();
// ConversationState and UserState
const conversationState = new ConversationState(memoryStorageLocal);
const userState = new UserState(memoryStorageLocal);
我获取如下的userID,它等于URL参数
for (var idx in turnContext.activity.membersAdded) {
if (turnContext.activity.membersAdded[idx].id !== turnContext.activity.recipient.id) {
console.log("User added");
this.userID = turnContext.activity.membersAdded[idx].id;
}
}
我读取和写入DB如下:
// Read userData object
try {
user = await this.memoryStorage.read([this.userID]);
console.log("User Object read from DB: " + util.inspect(user, false, null, true /* enable colors */));
}
catch(e) {
console.log("Reading user data failed");
console.log(e);
}
// If user is new, create UserData object and save it to DB and read it for further use
if(isEmpty(user)) {
await step.context.sendActivity("New User Detected");
this.changes[this.userID] = this.userData;
await this.memoryStorage.write(this.changes);
user = await this.memoryStorage.read([this.userID]);
}
如果用户是新用户,这是我保存到数据库的“空”userData对象:
this.userData = {
name: "",
age: "",
gender: "",
education: "",
major: "",
riskData: {
roundCounter: "",
riskAssessmentComplete: "",
riskDescription: "",
repeat: "",
choices: "",
},
investData: {
repeat: "",
order: "",
choice: "",
follow: "",
win1: "",
win2: "",
loss1: "",
loss2: "",
},
endRepeat: "",
eTag: '*',
}
对我而言,实际问题似乎不是我对CosmosDB的读写方式,而是因为用户ID和用户数据在同时使用时混淆了,因此没有不同的机器人实例。
我怎样才能做到这一点?你可以在这里找到我的机器人代码:
https://github.com/FRANZKAFKA13/roboadvisoryBot
在此先感谢您的帮助!
这是因为你在构造函数中存储了所有东西,只有机器人调用一次。两个用户同时使用this.changes
和this.userId
的相同实例。当一个用户更改它时,它也会更改为另一个用户。它基本上使它们成为任何用户都有能力改变的全局变量。
相反,您可以使用step.options
将所需的变量传递给对话框。
例如,
调用对话框:
await dc.beginDialog('welcome', userId)
在对话框中使用:
this.userId = step.options // It may also be step.options.userId, if you pass it in as part of a larger object, like .beginDialog('welcome', { userId: '123', userName: 'xyz' })
关于你最初尝试这个的方式,通过在构造函数中实例化this.userId
:
你可以在C#中实现这一点,因为在C#SDK中,每个消息都会调用构造函数(JS / TS只在首次启动机器人时调用它)。 BotFramework的JS和C#SDK之间存在这种主要差异,因为C#是多线程的,JS是单线程的。通过在C#中的每条消息上调用构造函数,您可以跨线程分散负载。这在JS / TS中是不可能的。
话虽这么说,我仍然会避免这种情况,因为在可能的情况下不使用“全局”变量是更好的代码实践。