C# 在bot框架中使用自适应对话框时,类对象bot状态是否可以保存在代码中?
我们在Bot框架中遇到了一个问题,当第一个步骤之一是执行C# 在bot框架中使用自适应对话框时,类对象bot状态是否可以保存在代码中?,c#,.net,botframework,C#,.net,Botframework,我们在Bot框架中遇到了一个问题,当第一个步骤之一是执行访问器.GetAsync()或.SetAsync()的CodeAction时,访问属性的后续对话框将因错误而崩溃: [OnTurnError]未处理的错误:无法将“Newtonsoft.Json.Linq.JValue”类型的对象转换为“System.String”类型。 完整堆栈如下所示: System.ArgumentException: Object of type 'Newtonsoft.Json.Linq.JValue' can
访问器.GetAsync()
或.SetAsync()
的CodeAction
时,访问属性的后续对话框将因错误而崩溃:
[OnTurnError]未处理的错误:无法将“Newtonsoft.Json.Linq.JValue”类型的对象转换为“System.String”类型。
完整堆栈如下所示:
System.ArgumentException: Object of type 'Newtonsoft.Json.Linq.JValue' cannot be converted to type 'System.String'.
at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
at System.Reflection.PropertyInfo.SetValue(Object obj, Object value)
at Microsoft.Bot.Builder.Dialogs.ObjectPath.SetObjectSegment(Object obj, Object segment, Object value, Boolean json)
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid5[T0,T1,T2,T3,T4](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
at Microsoft.Bot.Builder.Dialogs.ObjectPath.SetPathValue(Object obj, String path, Object value, Boolean json)
at Microsoft.Bot.Builder.Dialogs.Memory.DialogStateManager.SetValue(String path, Object value)
at Microsoft.Bot.Builder.Dialogs.Adaptive.Input.InputDialog.ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.DialogContext.ContinueDialogAsync(CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.Adaptive.AdaptiveDialog.ContinueActionsAsync(DialogContext dc, Object options, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.Adaptive.AdaptiveDialog.ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.DialogContext.ContinueDialogAsync(CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.DialogManager.HandleBotOnTurnAsync(DialogContext dc, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.DialogManager.OnTurnAsync(ITurnContext context, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.Dialogs.DialogManager.OnTurnAsync(ITurnContext context, CancellationToken cancellationToken)
at MyBot.Bot.MyBotBot`1.OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken) in H:\Work\MyBot\backendadaptivebot\api\Bots\MyBotBot.cs:line 50
at Microsoft.Bot.Builder.RegisterClassMiddleware`1.OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.RegisterClassMiddleware`1.OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.RegisterClassMiddleware`1.OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.BotFrameworkAdapter.TenantIdWorkaroundForTeamsMiddleware.OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.MiddlewareSet.ReceiveActivityWithStatusAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
at Microsoft.Bot.Builder.BotAdapter.RunPipelineAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
在RootDialog
类构造函数中,我们有:
participantAccessor = _userState.CreateProperty<Profile>("profile");
string[] paths = { ".", "Dialogs", $"RootDialog.lg" };
string fullPath = Path.Combine(paths);
// These steps are executed when this Adaptive Dialog begins
Triggers = new List<OnCondition>()
{
// Add a rule to welcome user
new OnConversationUpdateActivity()
{
Actions = WelcomeUserSteps()
},
// Respond to user on message activity
new OnUnknownIntent()
{
Actions = GetUserDetails()
},
};
此TextInput收到用户输入后,崩溃发生。
但是,如果删除了CodeAction
,则bot不会崩溃
在这个PopulateProfile
方法中,我们只需访问在构造函数中创建的属性,这是一个基本的配置文件类(只包含简单的属性:字符串和整数)
然后DialogContext.State中的对象类型似乎是JSON对象,并且bot按预期运行
这是否意味着Bot框架中的自适应对话框不支持通过代码将属性写入POCO类型
因此,必须通过简单的直接值(例如,user.profile.completed
)或通过SetProperty操作来完成
(我在任何地方都看不到该文件)
编辑:下面是简单的配置文件
类:
public class Profile
{
public string Id { get; set; }
public string AssociatedAsset { get; set; }
public string FullName { get; set; }
public string PreferredName { get; set; }
public string MobileNumber { get; set; }
public string Email { get; set; }
public int Complete { get; set; } = 0;
}
你试图将两种基本上不相容的做事方式结合起来。状态属性访问器是Bot Builder v4中访问状态的原始方式,但是自适应对话框是一个全新的系统,它们有自己的访问状态的方式 这就是问题所在。即使尝试将字符串分配给
此.value
,也会立即将该字符串转换为JToken
。因此,即使将JToken
作为字符串检索,当分配给user.profile.MobileNumber
时,它仍将转换回JToken
。如果user.profile
已使用类型信息序列化,则它将被反序列化为该类型,这意味着在JToken
被分配给其MobileNumber
属性之前,它将被转换为profile
对象。您可以在中提出这一错误,但请注意,您试图做的事情与自适应对话框的设计背道而驰
如果您想使用自适应对话框,您应该对此保持一致,并以自适应对话框的方式执行所有操作。代码操作很容易出错,因此应该谨慎使用。如果使用代码操作可以使用不同的自适应对话框操作,那么应该使用不同的操作,因为它将包含内置的自适应对话框功能。例如,如果您想设置一个属性,那么应该像您看到的那样使用SetProperty
如果确实要在代码操作中设置属性,则不应使用状态属性访问器,而应使用自适应对话框方式。自适应对话框使用SetValue
设置其属性,因此您也应该这样做。这将确保以自适应对话框可以轻松使用的方式格式化数据
private static async Task<DialogTurnResult> PopulateProfile(DialogContext dc, object options)
{
//var profile = await UserState.CreateProperty<Profile>("profile").GetAsync(dc.Context, () => new Profile());
//profile.Complete = 0;
dc.State.SetValue("user.profile.Complete", 0);
return await dc.EndDialogAsync(options);
}
私有静态异步任务PopulateProfile(DialogContext dc,对象选项)
{
//var profile=await UserState.CreateProperty(“profile”).GetAsync(dc.Context,()=>newprofile());
//profile.Complete=0;
dc.State.SetValue(“user.profile.Complete”,0);
return wait dc.EndDialogAsync(选项);
}
您试图将两种基本上不兼容的做事方式结合起来。状态属性访问器是Bot Builder v4中访问状态的原始方式,但是自适应对话框是一个全新的系统,它们有自己的访问状态的方式
这就是问题所在。即使尝试将字符串分配给此.value
,也会立即将该字符串转换为JToken
。因此,即使将JToken
作为字符串检索,当分配给user.profile.MobileNumber
时,它仍将转换回JToken
。如果user.profile
已使用类型信息序列化,则它将被反序列化为该类型,这意味着在JToken
被分配给其MobileNumber
属性之前,它将被转换为profile
对象。您可以在中提出这一错误,但请注意,您试图做的事情与自适应对话框的设计背道而驰
如果您想使用自适应对话框,您应该对此保持一致,并以自适应对话框的方式执行所有操作。代码操作很容易出错,因此应该谨慎使用。如果使用代码操作可以使用不同的自适应对话框操作,那么应该使用不同的操作,因为它将包含内置的自适应对话框功能。例如,如果您想设置一个属性,那么应该像您看到的那样使用SetProperty
如果确实要在代码操作中设置属性,则不应使用状态属性访问器,而应使用自适应对话框方式。自适应对话框使用SetValue
设置其属性,因此您也应该这样做。这将确保以自适应对话框可以轻松使用的方式格式化数据
private static async Task<DialogTurnResult> PopulateProfile(DialogContext dc, object options)
{
//var profile = await UserState.CreateProperty<Profile>("profile").GetAsync(dc.Context, () => new Profile());
//profile.Complete = 0;
dc.State.SetValue("user.profile.Complete", 0);
return await dc.EndDialogAsync(options);
}
私有静态异步任务PopulateProfile(DialogContext dc,对象选项)
{
//var profile=await UserState.CreateProperty(“profile”).GetAsync(dc.Context,()=>newprofile());
//profile.Complete=0;
dc.State.SetValue(“user.profile.Complete”,0);
return wait dc.EndDialogAsync(选项);
}
我在您的问题中看到了这三个不同的属性:profile.Complete
、profile.Completed
和profile.Completed
。我们能看看这个配置文件类是什么样子吗?还有,你为什么要这么做
public class Profile
{
public string Id { get; set; }
public string AssociatedAsset { get; set; }
public string FullName { get; set; }
public string PreferredName { get; set; }
public string MobileNumber { get; set; }
public string Email { get; set; }
public int Complete { get; set; } = 0;
}
private static async Task<DialogTurnResult> PopulateProfile(DialogContext dc, object options)
{
//var profile = await UserState.CreateProperty<Profile>("profile").GetAsync(dc.Context, () => new Profile());
//profile.Complete = 0;
dc.State.SetValue("user.profile.Complete", 0);
return await dc.EndDialogAsync(options);
}