Echo Bot .NET Core 3
Tags: echobotnetcore3
Published: 2019-11-27 00:59:09

Interesting news has come with ASP.NET Core 3.x. This project has been built to take advantage all of them; it allows to start at the typical Echo Bot, this time running already on .NET Core 3.

It's important to keep Visual Studio updated, due to .NET Core 3.x it's only supported starting at Visual Studio 2019 version 16.3

Clone [this](https://github.com/esalcedoo/EchoBotNETCore3) repo.

As a result, we´ll have a solution like this one:

![Solution explorer pointing at 4 classes we are going to explain next](https://dev.azure.com/esalcedoo/a9ddfdb1-0226-4f4f-a89d-42e9d69b4f3b/_apis/git/repositories/66d8fc7f-f7ae-4b26-a4aa-03920094c86a/Items?path=%2F/.attachments/image-5f490f7b-a8b0-4c88-90c3-a7dfec71aee0.png)

Let's see every class on it:

## 1.	Program
HostBuilder is created, as always, using startup class; built and run.
``` C
public static void Main(string[] args)
{
            CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
            webBuilder.UseStartup<Startup>();
});
```

## 2.	Startup
A Bot it's an API, thus, the Startup class configures services and the app's request pipeline.

At ConfigureServices method, EchoBot (an IBot implementation) is registered as transient; AdapterWithErrorHandler (an IBotFrameworkAdapter implementation) is registered as singleton.

``` C
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot,Bot>();
```
We will detailed both implementations later.
Configure method has changed while migrating to ASP.NET Core 3.x, now we use a routing middleware, it is on charge of analysing every route that comes in a http request.
Currently there is no need to use the whole MVC middleware; we can map each endpoint with the action it should run if the http request matches that route.
``` C
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.Map("api/messages", async context =>
    {
        var bot = context.RequestServices
                    .GetRequiredService<IBot>();
        var adapter = context.RequestServices
                    .GetRequiredService<IBotFrameworkHttpAdapter>();

        await adapter.ProcessAsync(context.Request, context.Response, bot);
    });
});
```

It is needed to set the endpoint as "api/messages" if we want to use the Bot Service.
When the action runs, the adapter will process the request.

## 3.	Adapter With Error Handler
BotFrameworkHttpAdapter connects Azure Bot Service with our Bot. Azure Bot Service would be the one that communicates with the channels on which the Bot is published.

The implementation AdapterWithErrorHandler of the adapter, extends its functionality to inform the user if an exception has been thrown.

## 4.	Bot
EchoBot inherits ActivityHandler (an IBot implementation).
ActivityHandler allows to override several methods that correspond with different situations on with the bot can interact with the user.

In this sample, we are managing two of these situations:
When someone joins the conversation, if it is not the bot itself (recipient), the bot greets:
``` C
protected override async Task OnMembersAddedAsync(
    IList<ChannelAccount> membersAdded, 
    ITurnContext<IConversationUpdateActivity> turnContext, 
    CancellationToken cancellationToken)
{
    foreach (var member in membersAdded)
    {
        if (member.Id != turnContext.Activity.Recipient.Id)
        {
            await turnContext.SendActivityAsync(
                MessageFactory.Text($"Welcome CommitConf2019!"),
                cancellationToken);
        }
    }
}
```
When a message is received, on this version of Echo Bot, it "Echoes" exactly what the user has said:
``` C
protected override async Task OnMessageActivityAsync (
    ITurnContext<IMessageActivity> turnContext, 
    CancellationToken cancellationToken)
{
    await turnContext.SendActivityAsync(
        MessageFactory.Text($"Echo: {turnContext.Activity.Text}"),
        cancellationToken);
}
```