.NET 6 - Minimal APIs  usando o Dapper


Hoje veremos como criar uma Minimal APIs no .NET 6.0 usando os recursos do Dapper.

O novo recurso do .NET 6.0 - Minimal APIs - para a ASP .NET Core 6 permite, criar APIs com o mínimo de dependência do framework WebAPI e o mínimo de código e arquivos necessários para o desenvolvimento minimalista de APIs.

Preparando o ambiente

Um dos objetivos do novo recurso Minimal APIs do NET 6 é facilitar o aprendizado para quem esta iniciando com a plataforma .NET.

Existe muita coisa a explorar com o advento das Minimal APIs e hoje irei apenas criar uma API mínima usando o Dapper.

Vou continuar usando o Visual Studio 2022 mas isso pode ser feito também usando a ferramenta NET CLI e o VS Code.

Assim nosso objetivo principal será :

A configuração do ambiente para o .NET 6 requer a instalação do .NET SDK 6.0.0-rc2 (já estamos na RC2) isso pode ser feito baixando o SDK para o ambiente Linux ou MacOs; para o Windows basta baixar o Visual Studio 2022 para já poder trabalhar com o NET 6.

Para este exemplo não vou usar o EF Core e assim vamos criar ou usar um banco de dados SQL Server já existente.

O script para criar a tabela Tarefas no banco de dados CadastroDB que iremos usar é dado a seguir:

USE [CadastroDB]
GO
CREATE TABLE [dbo].Tarefas(
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Atividade] [nvarchar](255),
	[Status] [nvarchar](100),
)
GO

Para este projeto iremos instalar os seguintes pacotes Nuget:

É claro que não queremos trilhar por este caminho e podemos resolver este problema refatorando o código e estruturando o projeto para obter um código mais robusto.

Criando o projeto no Visual Studio 2022

Como não há nenhum template ASP.NET Core específico para construir uma API mínima, vamos usar o template “ASP.NET Core Empty” para criar seu projeto no VS 2022:

Usando a ferramenta NET CLI podemos emitir o comando :   dotnet new web -o MinApi 

Criando o projeto no VS 2022, a seguir basta informar o nome do projeto que no nosso exemplo será : ApiTarefas

A seguir basta selecionar o Framework .NET 6.0 (que atualmente esta em RC2) e clicar em Create :

Teremos a solução será criada com a seguinte estrutura :

Observe que temos apenas o arquivo appsettings.json e o arquivo Program e a pasta Properties.

Abrindo o arquivo Program temos um código simplificado usando o recurso do Top Level Statements do C# 9.0.

Para mais detalhes sobre as Minimal APIs veja o meu artigo: ASP .NET Core 6 - Criando Minimal APIs - I

Agora vamos instalar os pacotes Nuget acionando o menu Tools -> ..-> Manage Nuget Package for Solution e selecionando o pacote na guia Browse(É importante deixar a opção include prerelease marcada)

Ao final seu arquivo de projeto (.csproj) deverá estar definido assim:

Agora temos tudo pronto para iniciar a criação da nossa Minimal API.

Para tornar o projeto mais estruturado eu vou criar as pastas:

Assim na pasta Data vamos criar o record Tarefa:

using System.ComponentModel.DataAnnotations.Schema;

[Table("Tarefas")]
public record Tarefa(int Id, string Atividade, string Status);

 

Criamos um tipo Record 'Tarefa' para usar como DTO e como Model.

Nesta pasta vamos criar a classe TarefaContext onde vamos definir  o delegate GetConnection() :

using System.Data;

public class TarefaContext
{
    public delegate Task<IDbConnection> GetConnection();
}

Criamos um delegate GetConnection que será usado na injeção de dependência para criar a conexão com o SQL Server.

Na pasta Extensions vamos criar o método de extensão AddPersistence na classe estática ServiceCollectionsExtensions:

using System.Data.SqlClient;
using static TarefaContext;

public static class ServiceCollectionsExtensions
{
    public static WebApplicationBuilder AddPersistence(this WebApplicationBuilder builder)
    {
        builder.Services.AddScoped<GetConnection>(sp =>
         async () =>
         {
             string connectionString = sp.GetService<IConfiguration>()["ConnectionString"];
             var connection = new SqlConnection(connectionString);
             await connection.OpenAsync();
             return connection;
         });

        return builder;
    }
}

Registramos o delegate GetConnection como um serviço de maneira que podemos obtê-lo mais tarde via injeção de dependência.

E para concluir vamos criar na pasta Endpoints a classe estática TarefasEndpoints onde teremos o método MapTarefasEndpoints onde vamos definir o mapeamento para os endpoints da nossa API usando os métodos auxiliares da classe WebApplication:

using Dapper.Contrib.Extensions;
using static TarefaContext;

public static class TarefasEndpoints
{
    public static void MapTarefasEndpoints(this WebApplication app)
    {
        app.MapGet("/", () => "Mini Tarefas API");

        app.MapGet("/tarefas", async (GetConnection connectionGetter) =>
        {
            using var con = await connectionGetter();
            return con.GetAll<Tarefa>().ToList();
        });

        app.MapGet("/tarefas/{id}", async (GetConnection connectionGetter, int id) =>
        {
            using var con = await connectionGetter();
            return con.Get<Tarefa>(id);
        });

        app.MapDelete("/tarefas/{id}", async (GetConnection connectionGetter, int id) =>
        {
            using var con = await connectionGetter();
            con.Delete(new Tarefa(id, "", ""));
            return Results.NoContent();
        });

        app.MapPost("/tarefas", async (GetConnection connectionGetter, Tarefa Tarefa) =>
        {
            using var con = await connectionGetter();
            var id = con.Insert(Tarefa);
            return Results.Created($"/tarefas/{id}", Tarefa);
        });

        app.MapPut("/tarefas", async (GetConnection connectionGetter, Tarefa Tarefa) =>
        {
            using var con = await connectionGetter();
            var id = con.Update(Tarefa);
            return Results.Ok();
        });

    }
}

Com isso a nossa solução ficou com a seguinte estrutura :

Agora podemos definir o código na classe Program conforme abaixo:

Ao invés de colocar todo o código no arquivo Program estruturamos o projeto criando pastas e separando em arquivos o código conforme a responsabilidade.

Temos assim um código mais estruturado e enxuto na classe Program.

Antes de executar precisamos definir no arquivo launchSettings da pasta Properties do projeto a definição para abrir o Swagger na execução:

...

 "profiles": {
    "ApiTarefas": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl" : "swagger",
      "applicationUrl": "https://localhost:7045;http://localhost:5045",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
...

E também precisamos definir no arquivo appsettings.json a string de conexão com o SQL Server:

{
  "ConnectionString":
"Data Source=DESKTOP-BHP8771\\SQLEXPRESS;Initial Catalog=CadastroDB;Integrated Security=True",
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Executando o projeto iremos obter os endpoints na interface do Swagger:

Agora basta testar cada endpoint verificando o funcionamento da API.

Pegue o projeto exemplo aqui:  ApiTarefas.zip (sem as referências)

"Portanto, irmãos, empenhem-se ainda mais para consolidar o chamado e a eleição de vocês, pois se agirem dessa forma, jamais tropeçarão, e assim vocês estarão ricamente providos quando entrarem no Reino eterno de nosso Senhor e Salvador Jesus Cristo."
2 Pedro 1:10,11

Referências:


José Carlos Macoratti