ASP.NET Core - Deploy no Heroku usando o PostgreSQL com EF Core


Hoje vou mostrar como publicar uma aplicação ASP .NET Core no Heroku acessando um banco de dados PostgreSQL e usando o EF Core.

O Heroku é uma PaaS - Platform as Service - ou seja uma plataforma em nuvem como um serviço que suporta várias linguagens de programação como Node, Ruby on Rails, Java, PHP, Phyton, Go, Scala e Clojure.

Para saber como fazer o deploy de uma aplicação ASP .NET Core no Heroku e conhecer quais os requisitos necessários veja o meu artigo : Heroku - Fazendo o deploy de uma Web API containerizada

Assim antes de continuar você vai precisar ter:

Assim neste artigo iremos realizar as seguintes tarefas:

Criando o projeto MVC

No VS 2022 crie um novo projeto usando o template ASP.NET Core Web App(Model-View-Controller) com o nome ProdutosCrud. usando o .NET 6.

A seguir instale os seguintes pacotes no projeto:

Para instalar você pode abrir uma janela do Package Manager Console via menu Tools no Visual Studio e digitar o comando : install-package <nome_pacote>

No projeto criado, na pasta Models, crie a classe Produto :

using System.ComponentModel.DataAnnotations;
namespace ProdutosCrud.Models;
public class Produto
{
    public int ProdutoId { get; set; }

    [Required(ErrorMessage = "O nome do produto é obrigatório")]
    [Display(Name = "Nome")]
    public string? ProdutoNome { get; set; }

    [Required(ErrorMessage = "O preço do produto é obrigatório")]
    public decimal Preco { get; set; }

    [Required(ErrorMessage = "O nome do estoque é obrigatório")]
    public int Estoque { get; set; }

    [Required(ErrorMessage = "O nome da imagem tem que ser informado")]
    public string? Imagem { get; set; }
}

Na mesma pasta cria o arquivo AppDbContext que vai herdar de DbContext e representa o arquivo de contexto que vai permitir criar uma sessão com o banco de dados.

using Microsoft.EntityFrameworkCore;
namespace ProdutosCrud.Models;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    { }

    public DbSet<Produto>? Produtos { get; set; }

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Produto>().HasKey(c => c.ProdutoId);
        mb.Entity<Produto>().Property(c => c.ProdutoNome).HasMaxLength(150);
        mb.Entity<Produto>().Property(c => c.Imagem).HasMaxLength(250);
        mb.Entity<Produto>().Property(c => c.Preco).HasPrecision(14, 2);
    }
}

Agora no arquivo appsettings.json vamos definir a string de conexão com o nosso PostgreSQL local :

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Port=5432;Pooling=true;Database=CadastroDB;User Id=***;Password=***;"
  },

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Vamos agora incluir o serviço do contexto no contêiner DI e configurar o ambiente para obter a string de conexão do PostgreSQL no Heroku.

No arquivo Program inclua o código abaixo:

using Microsoft.EntityFrameworkCore;
using ProdutosCrud.Models;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var IsDevelopment = Environment
                    .GetEnvironmentVariable("
ASPNETCORE_ENVIRONMENT") == "Development";

var connectionString = IsDevelopment ?
      builder.Configuration.GetConnectionString("DefaultConnection") :
     
GetHerokuConnectionString();

builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseNpgsql(connectionString)
);

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

static string GetHerokuConnectionString()
{
    string connectionUrl = Environment.GetEnvironmentVariable("
DATABASE_URL
");
    var databaseUri = new Uri(connectionUrl);

    string db = databaseUri.LocalPath.TrimStart('/');

    string[] userInfo = databaseUri.UserInfo
                        .Split(':', StringSplitOptions.RemoveEmptyEntries);

    return $"User ID={userInfo[0]};Password={userInfo[1]};Host={databaseUri.Host};" +
           $"Port={databaseUri.Port};Database={db};Pooling=true;" +
           $"SSL Mode=Require;Trust Server Certificate=True;";
}

Neste código estamos verificando se estamos no ambiente de desenvolvimento ou produção e conforme o caso iremos definir a string de conexão com os valores obtidos de appsettings.json ou fornecidos pelo ambiente do Heroku para fazer a conexão com PostgreSQL remoto.

Podemos agora aplicar o Migrations do EF Core para criar o banco e a tabela no PostgreSQL local digitando os comandos:

Após o processamento dos comandos acima podemos abrir o PgAdmin e verificar que o banco de dados e a tabela foram criadas conforme mostra a figura:

Esta etapa foi realizada apenas para termos certeza de que nossa aplicação vai funcionar localmente. Se você se sentir confortável pode pular esta etapa e aplicar o Migrations no PostgreSQL do Heroku.

Criando o controlador e as views

Vamos criar o controlador ProdutosController e as views para realizar o CRUD. Para isso vou usar o Scaffolding do VS 2022 clicando com o botão direito do mouse sobre a pasta Controllers do projeto e selecionado a opção Add -> Controller.

A seguir selecione a opção MVC Controller with views, using Entity Framework e clique em Add;

A seguir defina as opções conforme mostra a figura:

Clicando no botão Add teremos o controlador e as views criadas e após ajustar a view _Layout.cshtm poderemos acessar nosso projeto no navegador realizar o crud dos produtos.

Muito bem, tudo esta funcionando localmente e agora podemos pensar em criar o projeto no Heroku, adicionar o PostgreSQL remoto e configurar o ambiente para criar a imagem e publicar nosso projeto.

Criando o Dockerfile

Vamos criar o arquivo Dockerfile em nosso projeto para poder criar a imagem da nossa aplicação e publicar no Heroku.

Podemos fazer isso no VS 2022 clicando no projeto e selecionando a opção Add-> Docker Support. Com isso teremos o arquivo Dockerfile criado automaticamente.

Eu prefiro criar eu mesmo o arquivo e para isso vamos incluir um novo item no projeto e selecionar a opção Text File e informar o nome Dockerfile. A seguir vamos remover a extensão .txt do arquivo.

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app

# Copy csproj e restaurar as camadas
COPY *.csproj ./
RUN dotnet restore

# Copia tudo e builda
COPY . ./
RUN dotnet publish -c Release -o out

# Build com a imagem do runtime
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build-env /app/out .

# Usa porta dinâmica do Heroku
CMD ASPNETCORE_URLS="http://*:$PORT" dotnet ProdutosCrud.dll   
 

Estamos usando uma imagem oficial do net 6.0 para gerar a imagem da nossa aplicação. Observe que na última linha de código definimos um comando CMD onde estamos definindo a variável de ambiente ASPNETCORE_URLS para configurar a porta da aplicação que será fornecida pelo Heroku.

Na próxima parte do artigo iremos continuar criando a aplicação no Heroku.

"Se o SENHOR não edificar a casa, em vão trabalham os que a edificam; se o SENHOR não guardar a cidade, em vão vigia a sentinela."
Salmos 127:1

Referências:


José Carlos Macoratti