I am writing a bot and I want to create a step by step help system. I setup a dictionary that allow each dialog to overwrite the help text for each step in it:
public abstract class BaseDialog : ComponentDialog
{
private static Dictionary<string, string> StepHelp = new Dictionary<string, string>();
protected static void AddStepHelp(string function, string text)
{
StepHelp.Add(function, text);
}
private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken)
{
Activity helpMessage;
string curStepName = ""; //???
if (userText == "help" && StepHelp.ContainsKey(curStepName))
{
helpMessage = MessageFactory.Text(StepHelp[curStepName], StepHelp[curStepName], InputHints.ExpectingInput);
}
await innerDc.Context.SendActivityAsync(helpMessage, cancellationToken);
}
}
Then I add the text in the chidl dialog:
public class MyChildDialog: BaseDialog
{
static MyChildDialog()
{
AddStepHelp(nameof(Step1), "Help text for step1");
}
public MyChildDialog()
{
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
Step1
}));
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> Step1(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
}
You can see in the InterruptAsync function above that I call the dictionary to get the help message in the base dialog, but how do I get the current step name?
You can get the current step index as well as the dialog name, so you should be able to create a unique identifier from that information (as long as you didn't name all the dialogs "waterfallDialog" from the example like I did!). I'm using node but I'm assuming getting the data from your stepContext is similar or the same.
The dialog name can be found at stepContext.stack[stepContext.stack.length-1].id.
The step index can be found at stepContext.stack[stepContext.stack.length-1].state.stepIndex.
I can't recall if you can end up with nested dialogs inside a waterfall dialog. I know your main/outer context will have the whole stack, but I think you'll always just have the one element inside your particular waterfall. That said, the current dialog should be the last one the stack, so accessing it as stepContext.stack.length-1 should work in either case. In the event the current dialog is at element 0, obviously you could just access it as such.
So long as your waterfall dialog names are unique, you'd end up with identifiers like waterfallDialog0, waterfallDialog1, etc. that you could then map in your dictionary to help texts.
It occurred to me you might be trying to access this from outside the waterfall dialog. In that case you should still be able to get that from your outer dialog context. You would likely have to use a recursive function to get it, something like
getInnermostActiveDialog(dc) {
var child = dc.child;
return child ? this.getInnermostActiveDialog(child) : dc.activeDialog;
}
where dc is your outer dialog context. I haven't gone this deep into things but you should then be able to get the same id and stepIndex values.
While it's easy to get the step index as billoverton explained, getting the step name is difficult. The _steps field is private and so is the method that returns the step name so you won't be able to access the steps even if your class derives from WaterfallDialog. The step names are only exposed through the telemetry client so you might consider writing a custom telemetry client that somehow exposes the information to your bot but at that point it's probably easier just to use reflection to access the private members.
Since you only really want the step name to use as a key for your dictionary, just using the step index instead is a much better option. You could use a Dictionary<int, string> but it makes sense to use a List<string> if you intend to have a help string for every step in the waterfall.
Related
Question
How can I conditionally create and run a Dialog from
middleware without breaking the bot?
Context
I'm using the dotnetcore/13.core-bot sample.
I have a setup to run a custom spellchecking Middleware. I am trying to create a dialog from Middleware so that after the user types some misspelled input and ONLY when two or more spellcheck suggestions are found, the user gets the possible sentence interpretations and chooses from a HeroCard or similar.
From my middleware SpellcheckMiddleware.cs, myDialog.RunAsync(...) runs a dialog, however, after the middleware exits onTurnAsync(), I get a fatal error: "An item with the same key has already been added". That error occurs when the bot tries to continue MainDialog from MainDialog.cs which is the dialog that was setup in Startup.cs.
Bot emulator visual
Error capture within Visual Studio
("An item with the same key has already been added")
---
My code
The only thing I have changed from the sample code is creating these two files, one defines the dialog that resolves a spellcheck with multiple suggestions, and one that is middleware that should run the spellcheck dialog.
SpellcheckMiddleware.cs:
public class SpellCheckMiddleware : IMiddleware
{
private readonly ConversationState conversationState;
public SpellCheckMiddleware(
IConfiguration configuration,
ConversationState conversationState)
{
this.conversationState = conversationState;
}
public async Task OnTurnAsync(
ITurnContext turnContext,
NextDelegate next,
CancellationToken cancellationToken = new CancellationToken())
{
# Fake suggestions
List<List<String>> suggestions = new List<List<String>>{
new List<String>{'Info', 'Olympics'},
new List<String>{'Info', 'Olympia'},
};
SpellcheckSuggestionsDialog myDialog = new SpellcheckSuggestionsDialog(suggestions);
await myDialog.RunAsync(
turnContext,
conversationState.CreateProperty<DialogState>(nameof(DialogState)),
cancellationToken);
await next(cancellationToken);
}
}
SpellcheckSuggestionsDialog.cs:
class SpellcheckSuggestionsDialog : ComponentDialog
{
// Create a prompt that uses the default choice recognizer which allows exact matching, or number matching.
public ChoicePrompt SpellcheckPrompt { get; set; }
public WaterfallDialog WaterfallDialog { get; set; }
List<string> Choices { get; set; }
internal SpellcheckSuggestionsDialog(
IEnumerable<IEnumerable<string>> correctedSentenceParts)
{
SpellcheckPrompt = new ChoicePrompt(
nameof(ChoicePrompt),
validator: null,
defaultLocale: null);
WaterfallDialog = new WaterfallDialog(
nameof(WaterfallDialog),
new WaterfallStep[]{
SpellingSuggestionsCartesianChoiceAsync,
EndSpellingDialogAsync
});
AddDialog(SpellcheckPrompt);
AddDialog(WaterfallDialog);
InitialDialogId = nameof(WaterfallDialog);
// Get all possible combinations of the elements in the list of list. Works as expected.
var possibleUtterances = correctedSentenceParts.CartesianProduct();
// Generate a choices array with the flattened list
Choices = new();
foreach (var item in possibleUtterances) {
System.Console.WriteLine(item.JoinStrings(" "));
Choices.Add(item.JoinStrings(" "));
}
}
private async Task<DialogTurnResult> SpellingSuggestionsCartesianChoiceAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
{
return await stepContext.PromptAsync(
SpellcheckPrompt.Id,
new PromptOptions()
{
Choices = ChoiceFactory.ToChoices(Choices),
RetryPrompt = MessageFactory.Text("Did you mean...?"),
Prompt = MessageFactory.Text("Did you mean...?"),
Style = ListStyle.HeroCard
});
}
private async Task<DialogTurnResult> EndSpellingDialogAsync(
WaterfallStepContext stepContext,
CancellationToken cancellationToken)
{
// Overriding text sent using the choosen correction.
stepContext.Context.TurnState.Add("CorrectionChoice", stepContext.Result);
var choosen_correction = stepContext.Context.TurnState.Get<string>("CorrectionChoice");
stepContext.Context.Activity.Text = choosen_correction;
return await stepContext.EndDialogAsync(null, cancellationToken);
}
}
As of 2022, there is no sample from Microsoft showing dialogs being spawned from middleware, so that is likely not the intended way to use the framework. It may be possible, but then you're in a sense going against the framework, which is never advisable.
In order to have a dialog that provides spellcheck suggestions when the user types with a typo, I suggest you make that dialog part of logic specified in the WaterFall Dialog steps in MainDialog.cs.
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
SpellcheckStep, //new step to force user to choose a spellcheck suggestions
ActStepAsync,
FinalStepAsync,
}));
This comes with the drawback that if you need to spellcheck multiple user inputs in the conversation, then you will need to add multiple spellcheck steps, each customized to handle the input expected at the matching point in the conversation steps.
using (DiscordWebhookClient client = new DiscordWebhookClient(WEBHOOK_URL))
{
ulong z = 42342340290226;
client.ModifyMessageAsync(z);//Not sure how I would edit this message. The documentation is confusing.
}
Im not sure how to use this ModifyMessage function. Also, do I need to use a Async Function? I am just calling it without using any ASYNC. Im not sure if that is ok. The send message works, but second function im not sure how to work it.
Like others have said, you should await the async call. This ensures that the action is executed in a predictable fashion. The intend is to execute it right now and to wait for the result of the action.
That being said, the discord documentation describes this method as follows:
public Task ModifyMessageAsync(ulong messageId, Action<WebhookMessageProperties> func, RequestOptions options = null)
The second parameter describes a delegate based on WebhookMessageProperties. It can easily be defined by a lambda, like such:
x => { }
Now bear in mind the x is arbitrary, you can chose whatever designation you like, even whole words, I just kept it short for the example.
Between the bracelets, you can use access any properties that are in the WebhookMessageProperties class, by using x.SomeProperty. Where SomeProperty has to be a known property of that class if that makes sense.
One of the known properties is for example:
string Content { get; set; }
So here is how you can use a lambda to change the Content property:
using (DiscordWebhookClient client = new DiscordWebhookClient(WEBHOOK_URL))
{
ulong z = 42342340290226;
await client.ModifyMessageAsync(z, x =>
{
x.Content = "This is the updated message content";
});
}
If you want to update multiple properties at the same time, you can just add another line inside the lambda.
I have a controller which returns a large json object. If this object does not exist, it will generate and return it afterwards. The generation takes about 5 seconds, and if the client sent the request multiple times, the object gets generated with x-times the children. So my question is: Is there a way to block the second request, until the first one finished, independent who sent the request?
Normally I would do it with a Singleton, but because I am having scoped services, singleton does not work here
Warning: this is very oppinionated and maybe not suitable for Stack Overflow, but here it is anyway
Although I'll provide no code... when things take a while to generate, you don't usually spend that time directly in controller code, but do something like "start a background task to generate the result, and provide a "task id", which can be queried on another different call).
So, my preferred course of action for this would be having two different controller actions:
Generate, which creates the background job, assigns it some id, and returns the id
GetResult, to which you pass the task id, and returns either different error codes for "job id doesn't exist", "job id isn't finished", or a 200 with the result.
This way, your clients will need to call both, however, in Generate, you can check if the job is already being created and return an existing job id.
This of course moves the need to "retry and check" to your client: in exchange, you don't leave the connection to the server opened during those 5 seconds (which could potentially be multiplied by a number of clients) and return fast.
Otherwise, if you don't care about having your clients wait for a response during those 5 seconds, you could do a simple:
if(resultDoesntExist) {
resultDoesntExist = false; // You can use locks for the boolean setters or Interlocked instead of just setting a member
resultIsBeingGenerated = true;
generateResult(); // <-- this is what takes 5 seconds
resultIsBeingGenerated = false;
}
while(resultIsBeingGenerated) { await Task.Delay(10); } // <-- other clients will wait here
var result = getResult(); // <-- this should be fast once the result is already created
return result;
note: those booleans and the actual loop could be on the controller, or on the service, or wherever you see fit: just be wary of making them thread-safe in however method you see appropriate
So you basically make other clients wait till the first one generates the result, with "almost" no CPU load on the server... however with a connection open and a thread from the threadpool used, so I just DO NOT recommend this :-)
PS: #Leaky solution above is also good, but it also shifts the responsability to retry to the client, and if you are going to do that, I'd probably go directly with a "background job id", instead of having the first (the one that generates the result) one take 5 seconds. IMO, if it can be avoided, no API action should ever take 5 seconds to return :-)
Do you have an example for Interlocked.CompareExchange?
Sure. I'm definitely not the most knowledgeable person when it comes to multi-threading stuff, but this is quite simple (as you might know, Interlocked has no support for bool, so it's customary to represent it with an integral type):
public class QueryStatus
{
private static int _flag;
// Returns false if the query has already started.
public bool TrySetStarted()
=> Interlocked.CompareExchange(ref _flag, 1, 0) == 0;
public void SetFinished()
=> Interlocked.Exchange(ref _flag, 0);
}
I think it's the safest if you use it like this, with a 'Try' method, which tries to set the value and tells you if it was already set, in an atomic way.
Besides simply adding this (I mean just the field and the methods) to your existing component, you can also use it as a separate component, injected from the IOC container as scoped. Or even injected as a singleton, and then you don't have to use a static field.
Storing state like this should be good for as long as the application is running, but if the hosted application is recycled due to inactivity, it's obviously lost. Though, that won't happen while a request is still processing, and definitely won't happen in 5 seconds.
(And if you wanted to synchronize between app service instances, you could 'quickly' save a flag to the database, in a transaction with proper isolation level set. Or use e.g. Azure Redis Cache.)
Example solution
As Kit noted, rightly so, I didn't provide a full solution above.
So, a crude implementation could go like this:
public class SomeQueryService : ISomeQueryService
{
private static int _hasStartedFlag;
private static bool TrySetStarted()
=> Interlocked.CompareExchange(ref _hasStartedFlag, 1, 0) == 0;
private static void SetFinished()
=> Interlocked.Exchange(ref _hasStartedFlag, 0);
public async Task<(bool couldExecute, object result)> TryExecute()
{
if (!TrySetStarted())
return (couldExecute: false, result: null);
// Safely execute long query.
SetFinished();
return (couldExecute: true, result: result);
}
}
// In the controller, obviously
[HttpGet()]
public async Task<IActionResult> DoLongQuery([FromServices] ISomeQueryService someQueryService)
{
var (couldExecute, result) = await someQueryService.TryExecute();
if (!couldExecute)
{
return new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = "Another request has already started. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
return Ok(result);
}
Of course possibly you'd want to extract the 'blocking' logic from the controller action into somewhere else, for example an action filter. In that case the flag should also go into a separate component that could be shared between the query service and the filter.
General use action filter
I felt bad about my inelegant solution above, and I realized that this problem can be generalized into basically a connection number limiter on an endpoint.
I wrote this small action filter that can be applied to any endpoint (multiple endpoints), and it accepts the number of allowed connections:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConcurrencyLimiterAttribute : ActionFilterAttribute
{
private readonly int _allowedConnections;
private static readonly ConcurrentDictionary<string, int> _connections = new ConcurrentDictionary<string, int>();
public ConcurrencyLimiterAttribute(int allowedConnections = 1)
=> _allowedConnections = allowedConnections;
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var key = context.HttpContext.Request.Path;
if (_connections.AddOrUpdate(key, 1, (k, v) => ++v) > _allowedConnections)
{
Close(withError: true);
return;
}
try
{
await next();
}
finally
{
Close();
}
void Close(bool withError = false)
{
if (withError)
{
context.Result = new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = $"Maximum {_allowedConnections} simultaneous connections are allowed. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
_connections.AddOrUpdate(key, 0, (k, v) => --v);
}
}
}
I have a WPF application that I'm starting to develop. I have a 40 or so methods that are accessible through the UI, but also need to be executed by passing parameters via the command line.
Currently i have the following, that allows me to catch the arguments on the App.xaml.cs...
public partial class App : Application
{
string[] args = MyApplication.GetCommandLineArgs();
Dictionary<string, string> dictionary = new Dictionary<string, string>();
private void Application_Startup(object sender, StartupEventArgs e)
{
for (int index = 1; index < args.Length; index += 2)
{
dictionary.Add(args[index], args[index + 1]);
}
if (dictionary.Keys.Contains("/Task"))
{
MessageBox.Show("There is a Task");
}
}
}
}
I am looking to pass a argument at the start of every call through the command line. If i pass
/Task ThisIsTheTask
I can read this from the dictionary. And then execute the related method.
My question is what is the best way of "routing" the task parameter to a specific method. I will also be passing additional parameters after the task that will need to be passed to the method.
It could be considered an implementation of the service-locator anti-pattern, but one simple approach would be to have something like the following:
private readonly Dictionary<string, Action<string[]>> commands = new Dictionary<string, Action[]>
{
{"Task1", args => Task1Method(args[0], Int32.Parse(args[1]))}
}
private static Task1Method(string firstArgs, int secondArg)
{
}
Your code can then locate an Action<string[]> for the task specified on the command line, and pass the remaining parameters to the Action, e.g.
var commandLineArgs = Environment.GetCommandLineArgs();
var taskName = commandLineArgs[1];
// Locate the action to execute for the task
Action<string[]> action;
if(!commands.TryGetValue(taskName, out action))
{
throw new NotSupportedException("Task not found");
}
// Pass only the remaining arguments
var actionArgs = new string[commandLineArgs.Length-2];
commandLineArgs.CopyTo(actionArgs, 2);
// Actually invoke the handler
action(actionArgs);
If you are able to use third-party, open source libraries I would suggest taking a look at ManyConsole, it is available via NuGet here.
ManyConsole allows you to define ConsoleCommand implementations (see here for an example implementation), which can have many parameters. You are then able to use a ConsoleCommandDispatcher to route to the appropriate ConsoleCommand implementation based upon the command-line arguments (see here for an example).
I am in no way affiliated with ManyConsole, but I have used the library and found it to be very effective.
I'd suggest one (or more) properties on the application class of your program that expose them. Access during runtime can then be done by using something like
(Application.Current as App).MyTask
which can then be further wrapped for convenience.
Also, you can write your own "Main" method in WPF too - that way you would have easier access to the parameters array and could do processing before WPF starts up if you need to. I'll edit in how if you need that.
I have the following code which I am are currently using .... Basically, this method assigns the correct boolean flag (TRUE/FALSE) for each Task. As more and more tasks need to be added .. I can see that the switch statement will have to grow to cater for every task.
There has to be an easier way ... to keep the method small.
Code: (forget naming convention, it has been changed for posting)
public ClassStructure.User AssignTaskStatusToUser(ClassStructure.User,
List<ClassStructure.Tasks> TaskStatus)
{
foreach (ClassStructure.Tasks data in TaskStatus)
{
string Task_CallID = data.Task_Call_ID;
switch (Task_CallID)
{
case ClassStructure.Tasks_CallIDs_Strings.TASK1:
User.TASK1 = data.Task_Flag;
break;
case ClassStructure.Tasks_CallIDs_Strings.TASK2:
User.TASK2 = data.Task_Flag;
break;
case ClassStructure.Tasks_CallIDs_Strings.TASK3:
User.TASK3 = data.Task_Flag;
break;
}
}
return User;
}
ClassStructure.Tasks_CallIDs_Strings = String Representation of the Tasks
data.Task_Flag = boolean
User.TASKX = boolean
Any feedback is welcome. I am sure there is an easy solution.
For a lot of values like these, I would use a map something like this:
Dictionary<ClassStructure.Tasks_CallIDs_Strings, Task_Flag>
and retrieve values by mapping the CallIDs strings.
Edit:
As everyone can now see, the real problem of refactoring this example lies in refactoring User.TASKX. Making it a list should suffice - as it could then be indexed by the same string ClassStructure.Tasks_CallIDs_Strings
Oh... Reconsider your naming scheme.
public delegate void TaskAssigner(User user, bool taskFlag)
IDictionary<string, TaskAssigner> taskAssigners = new Dictionary<string, TaskAssigner>();
...
taskAssigners.Add(ClassStructure.Tasks_CallIDs_Strings.TASK1, (u, t) => u.TASK1 = t;);
taskAssigners.Add(ClassStructure.Tasks_CallIDs_Strings.TASK2, (u, t) => u.TASK2 = t;);
...
foreach(ClassStructure.Tasks data in TaskStatus)
taskAssigners[data.Task_Call_ID](user, data.Task_Flag);
I was thinking something like this - but maybe I missed the point of what it is all for?
public class User
{
private Dictionary<string,Task> tasks;
internal Dictionary<string,Task> Tasks
{
get { return tasks; }
set { tasks = value; }
}
internal void AddTask(Task task)
{
tasks.Add(task.Task_Call_ID,task);
}
internal void AddTasks(List<Task> task)
{
foreach(Task task in Tasks)
{
tasks.Add(task.Task_Call_ID,task);
}
}
}
The Task class could have properties that allowed you to pass a function pointer (to the function that actually executes a task) if you needed that kind of flexibility - and you could add other methods like ExecuteTasks to User as well...
Could you have an array/list of tasks instead and use Task_CallID as an index into that?
e.g.
User.Tasks[Task_CallID] = data.Task_Flag;
If you must have them all as members there are other options:
Maintain a mapping from Task_Call_ID to PropertyInfo reference and use that to set the correct property
Use reflection to find the property based on the number bit (X) and set that property
Both of these are reflection based and a bit nasty.
Why not make a Users Tasks structured as a list:
User Class
public List<ClassStructure.Tasks> Tasks {
get; set;
}
Your Method becomes:
public void AssignTasks(User user, List<ClassStructure.Tasks> TaskStatus)
{
user.Tasks.AddRange(TaskStatus)
}
Which is to say that you don't need the method at all.
Your accessor then becomes running Find on a user's Tasks and checking the Tasks flag.
Dictionary is a great alternative for this. However, when a switch/case gets very complex look at using the strategy pattern (not for your scenario though).