Come dovrei progettare un bot C # che può essere esteso con modifiche minime al codice?

0

Sto progettando un bot C # utilizzando il Microsoft Bot Framework . Mi piacerebbe che il bot fosse facilmente estensibile, consentendo agli sviluppatori di aggiungere dialoghi / risposte mentre si apportano modifiche al codice nel minor numero possibile di posizioni. Dai miei esperimenti e guardando gli esempi nel repository BotBuilder di Microsoft , sembra di aggiungere un nuovo dialogo / risposta richiede l'aggiunta di codice in due punti: un dispatcher in MessageController o RootDialog, più il codice effettivo che implementa la finestra di dialogo. C'è un modo per strutturare il codice in modo che le estensioni richiedano solo l'aggiunta di codice in un unico posto?

Codice di esempio:

Da MessagesController.cs:

[...]
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
    if (activity.Type == ActivityTypes.Message)
    {
        await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
     }
     else
     {
         HandleSystemMessage(activity);
     }
     var response = Request.CreateResponse(HttpStatusCode.OK);
     return response;
 }
[...]

Questo metodo è il principale punto di accesso al bot; tutti i messaggi che riceve attivano una chiamata POST a questo endpoint. In questo codice, ciò fa semplicemente scattare la creazione e l'esecuzione di RootDialog, ma potrebbe essere scritto per attivare una finestra di dialogo diversa a seconda dei dettagli del messaggio.

Da RootDialog.cs:

[...]
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
    var activity = await result as Activity;

    if (activity.Text == "command-one")
    {
        await context.PostAsync("Received command one.");
    } 
    else if (activity.Text == "command-two")
    {
        someOtherObject.doSomething();
        await context.PostAsync("Received command two.");
    }
    else
    {
        // calculate something for us to return
        int length = (activity.Text ?? string.Empty).Length;

        // return our reply to the user
        await context.PostAsync($"You sent {activity.Text} which was {length} characters");
    }

    context.Wait(MessageReceivedAsync);
}
[...]

Questo metodo fa cose diverse a seconda del testo del messaggio. Nel caso di un comando, mostra solo una semplice risposta. Le estensioni lungo queste linee richiedono solo l'aggiunta di codice a questo metodo, che va bene. Nel caso del secondo comando, chiama il metodo di qualche altra classe, che può eseguire qualche altra attività arbitraria. L'aggiunta di un'estensione come questa richiede un'altra classe e l'aggiunta di un dispatcher a RootDialog. Per riaffermare la mia domanda, c'è un modo per evitare di dover aggiungere il dispatcher in RootDialog, magari aggiungendo qualche sorta di annotazione ai metodi in altre classi?

    
posta DylanSp 16.05.2017 - 20:51
fonte

1 risposta

2

L'idea: mappare i nomi dei comandi ai comandi attuali, eseguiti per nome.

Sto abusando di System.Window.Input.ICommand qui per mostrare il pattern; è possibile utilizzare una personalizzazione o un'altra interfaccia esistente.

Dictionary<string, ICommand> COMMANDS = ...;

// ...

if (COMMANDS.ContainsKey(command_name)) {
  COMMANDS[command_name].Invoke(...); // Pass any context a command might need.
} else {
  // Handle 'Command not found' situation.
}

Costruirei COMMANDS interpretando un file di configurazione, (probabilmente all'avvio, a meno che non si carichi dinamicamente gli assembly per le implementazioni dei comandi, allora si potrebbe farlo periodicamente). Ciò consente di abilitare / disabilitare selettivamente e rinominare i comandi senza ricostruire il codice.

    
risposta data 17.05.2017 - 20:22
fonte

Leggi altre domande sui tag