/// <summary>
        /// Called when the dialog is started and pushed onto the dialog stack.
        /// </summary>
        /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
        /// <param name="options">Optional, initial information to pass to the dialog.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options is CancellationToken)
            {
                throw new ArgumentException($"{nameof(options)} cannot be a cancellation token");
            }

            if (this.Disabled != null && this.Disabled.GetValue(dc.State) == true)
            {
                return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false));
            }

            lock (this.Condition)
            {
                if (this.caseExpressions == null)
                {
                    this.caseExpressions = new Dictionary <string, Expression>();

                    foreach (var @case in this.Cases)
                    {
                        if (long.TryParse(@case.Value, out long intVal))
                        {
                            // you don't have to put quotes around numbers, "23" => 23 OR "23"
                            this.caseExpressions[@case.Value] = Expression.OrExpression(
                                Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(intVal)),
                                Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(@case.Value)));
                        }
                        else if (float.TryParse(@case.Value, out float floatVal))
                        {
                            // you don't have to put quotes around numbers, "23" => 23 OR "23"
                            this.caseExpressions[@case.Value] = Expression.OrExpression(
                                Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(floatVal)),
                                Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(@case.Value)));
                        }
                        else if (bool.TryParse(@case.Value, out bool boolVal))
                        {
                            // you don't have to put quotes around bools, "true" => true OR "true"
                            this.caseExpressions[@case.Value] = Expression.OrExpression(
                                Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(boolVal)),
                                Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(@case.Value)));
                        }
                        else
                        {
                            // if someone does "=23" that will be numeric comparison or "='23'" that will be string comparison, or it can be a
                            // real expression bound to memory.
                            var(value, _) = new ValueExpression(@case.Value).TryGetValue(dc.State);
                            this.caseExpressions[@case.Value] = Expression.EqualsExpression(this.Condition, Expression.ConstantExpression(value));
                        }
                    }
                }
            }

            ActionScope actionScope = this.DefaultScope;

            foreach (var caseScope in this.Cases)
            {
                var(value, error) = this.caseExpressions[caseScope.Value].TryEvaluate(dc.State);

                // Compare both expression results. The current switch case triggers if the comparison is true.
                if (value != null && ((bool)value) == true)
                {
                    actionScope = caseScope;
                    break;
                }
            }

            return(await dc.ReplaceDialogAsync(actionScope.Id, null, cancellationToken : cancellationToken).ConfigureAwait(false));
        }
        public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (options is CancellationToken)
            {
                throw new ArgumentException($"{nameof(options)} cannot be a cancellation token");
            }

            var dcState = dc.GetState();

            if (this.Disabled != null && this.Disabled.GetValue(dcState) == true)
            {
                return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false));
            }

            lock (this.Condition)
            {
                if (this.caseExpressions == null)
                {
                    this.caseExpressions = new Dictionary <string, Expression>();

                    foreach (var cse in this.Cases)
                    {
                        Expression caseExpression = null;

                        // Values for cases are always coerced to constant
                        if (long.TryParse(cse.Value, out long i))
                        {
                            caseExpression = Expression.ConstantExpression(i);
                        }
                        else if (float.TryParse(cse.Value, out float f))
                        {
                            caseExpression = Expression.ConstantExpression(f);
                        }
                        else if (bool.TryParse(cse.Value, out bool b))
                        {
                            caseExpression = Expression.ConstantExpression(b);
                        }
                        else
                        {
                            caseExpression = Expression.ConstantExpression(cse.Value);
                        }

                        var caseCondition = Expression.EqualsExpression(this.Condition, caseExpression);

                        // Map of expression to actions
                        this.caseExpressions[cse.Value] = caseCondition;
                    }
                }
            }

            ActionScope actionScope = this.DefaultScope;

            foreach (var caseScope in this.Cases)
            {
                var(value, error) = this.caseExpressions[caseScope.Value].TryEvaluate(dcState);

                if (error != null)
                {
                    throw new Exception($"Expression evaluation resulted in an error. Expression: {caseExpressions[caseScope.Value].ToString()}. Error: {error}");
                }

                // Compare both expression results. The current switch case triggers if the comparison is true.
                if (((bool)value) == true)
                {
                    actionScope = caseScope;
                    break;
                }
            }

            return(await dc.ReplaceDialogAsync(actionScope.Id, null, cancellationToken : cancellationToken).ConfigureAwait(false));
        }