EsbenB
EsbenB

Reputation: 3406

MS Bot builder IForm Invalid Type Exception:Has xxx Expect yyy

I'm trying to build a Simple iForm Based bot. When one Form is completed I want to show another. Both Forms consist of a single enum value. The first form show correctly. When I select a option, the SelectNextAction is called as expected. The first dialog is constructed via:

  public static IForm<RootForm> BuildRootForm()
    {
        IForm<RootForm> ret  = new FormBuilder<RootForm>()
             .Message("Hi")
             .AddRemainingFields()
             .OnCompletion(SelectNextAction)
                .Build();

        return ret;
    }

    private static async Task SelectNextAction(IDialogContext context, RootForm state)
    {

        // Tell the user that the form is complete
        switch (state.Choice)
        {
            case MainChoice.Transfer:
                IFormDialog<TransferForm> form = FormDialog.FromForm(TransferForm.BuildTransferForm);
                context.Call(form, TransferDialogComplete);
                break;
            default:
                break;

        }
    }

After this code has run TransferDialogComplete is called:

   private async static Task TransferDialogComplete(IDialogContext context, IAwaitable<TransferForm> result)
    {
        await result;
    }

And then a Exception is thrown:

Exception: invalid type: expected EBBot.Dialogs.TransferForm, have RootForm [File of type 'text/plain']

What am I doing wrong? any idea or suggestions?

Upvotes: 0

Views: 298

Answers (2)

Ezequiel Jadib
Ezequiel Jadib

Reputation: 14787

As I mentioned in the comments, the issue is due to the call of the second form in the OnCompletion call.

In order to solve this, you should call the second form in the ResumeAfter<T> method that you specified when you call your first form.

Code calling RootForm

var rootForm = FormDialog.FromForm(RootForm.BuildRootForm);
context.Call(rootForm, AfterRootFormCompleted);

Code to execute after RootForm completes

private async Task AfterRootFormCompleted(IDialogContext context, IAwaitable<RootForm> result)
{
     try 
     {
        var state = await result;
        IFormDialog<TransferForm> form = FormDialog.FromForm(TransferForm.BuildTransferForm);
        context.Call(form, TransferDialogComplete);
     }
     catch (FormCancelledException)
     {
      // do something;
     }
}

Upvotes: 1

Xeno-D
Xeno-D

Reputation: 2213

The delegate passed into the OnCompletion method can not be used to call another forms or any other dialogs.

This should only be used for side effects such as calling your service wit the form state results. In any case the completed form state will be passed to the parent dialog.

Source: line 225

For an example look here.

How you can implement this is through the ResumeAfter method. Here is an example:

[Serializable]
public class RootDialog : IDialog<object>
{
    public async Task StartAsync(IDialogContext context)
    {
        var form = new FormDialog<SimpleForm>(new SimpleForm1(), 
        SimpleForm1.BuildForm);
        context.Call(form, ResumeAfterForm1);
    }

    private async Task ResumeAfterForm1(IDialogContext context, IAwaitable<object> result)
    {
        var form = new FormDialog<SimpleForm2>(new SimpleForm2(), 
        SimpleForm2.BuildForm, FormOptions.PromptInStart);
        context.Call(form, ResumeAfterForm2);
    }
    // other code
}

Note: I used the PromptInStart Because I have noticed that if you don't include this the form will not start right away.

Upvotes: 1

Related Questions