/// <summary> /// Get prompt waterfall steps for info collection /// </summary> /// <param name="info">property info</param> /// <returns>List of waterfall steps for property collection</returns> private IEnumerable <WaterfallStep> GetPromptWaterfallSteps() { var steps = new List <WaterfallStep>(); // Add steps to get properties and set previous one PromptPropertyInfo previousProp = null; foreach (var prop in promptProperties) { steps.Add(this.BuildWaterfallStep(previousProp, prop)); previousProp = prop; } // Add step to set last property steps.Add(this.BuildWaterfallStep(previousProp, null)); return(steps); }
/// <summary> /// Get a WaterfallStep given previous property and current property which need to be collected. /// The waterfall step will record previous property value and prompt for new property value when needed. /// NOTE - we need this function since we need eager evaluation of previousProp and currentProp. /// </summary> /// <param name="previousProp">previous property which has already been prompted. null means current prop is the first one</param> /// <param name="currentProp">current property which needs to be prompted. null means no more.</param> /// <returns>A waterstep delegate which does the real work</returns> private WaterfallStep BuildWaterfallStep(PromptPropertyInfo previousProp, PromptPropertyInfo currentProp) { return(async(WaterfallStepContext stepContext, CancellationToken cancellationToken) => { var intentContext = stepContext.Options as TContext; // Try to save the value collected from previous step if (previousProp?.Setter != null) { // We want the property to be nullable so that we can check whether it's set or not. // However, we cannot convert stepContext.Result of type object with int value to int? using Convert.ChangeType. // So we try to convert stepContext.Result to the underlying non nullable type first. // The Setter will handle non-nullable to nullable conversion automatically. var targetNonNullableType = Nullable.GetUnderlyingType(previousProp.PropertyType) ?? previousProp.PropertyType; var convertedValue = Convert.ChangeType(stepContext.Result, targetNonNullableType); previousProp.Setter(intentContext, convertedValue); } object currentPropertyValue = null; if (currentProp?.Getter != null) { // Collect info for current property. // We do null check to see whether the value has been set or not. // This means that the corresponding IntentContext property needs to be "nullable". currentPropertyValue = currentProp.Getter(intentContext); if (currentPropertyValue == null) { // current property is not collected yet. Prompt and collect return await stepContext.PromptAsync(currentProp.PromptDialog.Id, currentProp.PromptOptions, cancellationToken); } } // If we reach here, it's either no new info needs to be collected (currentGetter == null), // or current property is already set (currentProperty != null). // Move to next step and pass the currentProperty value. return await stepContext.NextAsync(currentPropertyValue, cancellationToken); }); }