ASP .NET Core - Web API com SQL Server em um Contêiner Docker - I
Hoje veremos como criar uma Web API ASP.Net Core acessando o SQL Server e executar a aplicação em um contêiner Docker. |
Hoje vamos iniciar uma série de artigos que aborda a utilização da criação de contêineres Docker para aplicações ASP .NET Core e para o Sql Server.
Objetivos e requisitos
Vamos criar uma aplicação ASP .NET Core WEb API para gerenciar informações sobre Produtos como : Id, Name, Description e Price, fazendo um CRUD básico no SQL Server, e vamos conteinerizar esta aplicação API.
Nesta aplicação vamos usar o EF Core, e vamos usar o SQL Server em um contêiner. Assim teremos dois contêineres : a aplicação Web API que vai acessar o contêiner do banco de dados SQL Server.
Os recursos que iremos usar são todos grátis:
A primeira a coisa a fazer é instalar ao .NET Core SDK 5.0 no seu ambiente.
Após a instalação, para verificar o ambiente abra uma janela do PowerShell e digite o comando: dotnet --version
Com o .NET Core SDK instalado você pode instalar o Visual Studio 2019 Community que é grátis e a seguir instalar o Docker Desktop for Windows que também é grátis e iniciar o desenvolvimento.
Criando a Web API ASP .NET Core
Vamos criar uma solução em branco usando o template Blank Solution com o nome EFCoreDocker:
A seguir no menu File selecione Add-> New Project e selecione o template ASP .NET Core Web API e clique em Next;
Informe o nome EFCoreSqlServer e clique em Next;
A seguir selecione o Target Framework, Authentication Type e demais configurações conforme mostrada na figura:
Note que não habilitamos o HTTPS para evitar ter que habilitar um certificado no contêiner.
Agora vamos remover os arquivos WeatherForecastController.cs e WeatherForecast.cs do projeto criado.
A seguir inclua no projeto uma referência ao pacote Microsoft.EntityFrameworkCore.SqlServer abrindo a janela Package Manager Console e digitando o comando:
install-package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.5
Crie uma pasta Entities no projeto e a seguir inclua nesta pasta a classe Product que representa a nossa entidade de domínio:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
}
|
Crie uma pasta DataContext e inclua nesta pasta a classe ApplicationDbContext :
using EFCoreSqlServer.Entities; using Microsoft.EntityFrameworkCore;
namespace EFCoreSqlServer.DataContext
public DbSet<Product> Products { get; set; } |
Definimos o arquivo de contexto que realiza o mapeamento ORM e permite definir as opções do contexto informando a string de conexão e o provedor do banco de dados.
O método Database.EnsureCreated() garante que o banco de dados para o contexto exista. Se ele existir, nenhuma ação será realizada. Se ele não existir, o banco de dados e todo o seu esquema serão criados.
Configurando o serviço e a string de conexão
Vamos registrar o contexto como um serviço no arquivo Startup e definir o provedor e a string de conexão com o SQL Server que iremos usar.
No método ConfigureServices inclua o código a seguir:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SqlConnection")));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "EFCoreSqlServer", Version = "v1" });
});
}
|
Agora abra o arquivo appsettings.json e crie a seção ConnectionStrings definindo a string de conexão com o nome do banco de dados, o usuário e a senha para acessar o SQL Server.
{
"ConnectionStrings": {
"SqlConnection": "Server=sqlserver;Database=ProductsDb;User Id=sa;Password=Numsey#2021"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
|
Criando a API - ProductsController
Agora, Inclua um novo Controller na pasta Controllers chamado ProductsController com o seguinte código:
using EFCoreSqlServer.DataContext;
using EFCoreSqlServer.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace EFCoreSqlServer.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly ApplicationDbContext _productsDbContext;
public ProductsController(ApplicationDbContext productsDbContext)
{
_productsDbContext = productsDbContext;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Product>>> GetProduct()
{
return Ok(await _productsDbContext.Products.ToListAsync());
}
[HttpPost]
public async Task<ActionResult<Product>> Create([FromBody] Product product)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
await _productsDbContext.Products.AddAsync(product);
await _productsDbContext.SaveChangesAsync();
return Ok(product);
}
[HttpPut("{id}")]
public async Task<ActionResult<Product>> Update(int id, [FromBody] Product productFromJson)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var product = await _productsDbContext.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
product.Name = productFromJson.Name;
product.Price = productFromJson.Price;
product.Description = productFromJson.Description;
await _productsDbContext.SaveChangesAsync();
return Ok(product);
}
[HttpDelete("{id}")]
public async Task<ActionResult<Product>> Delete(int id)
{
var product = await _productsDbContext.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
_productsDbContext.Remove(product);
await _productsDbContext.SaveChangesAsync();
return Ok(product);
}
}
}
|
No código da nossa API injetamos uma instância do contexto no construtor e assim usaremos essa instância para realizar as operações no SQL Server usando o EF Core.
A seguir criamos os métodos que definem os endpoints da API e realizam um CRUD onde podemos incluir, consultar, excluir e editar produtos.
Criando o Dockerfile e Docker Compose
Vamos agora criar o arquivo Dockerfile no projeto onde teremos os comandos para criar a imagem da nossa aplicação ASP .NET Core.
Após criar a imagem podemos criar o contêiner da aplicação a partir da imagem:
Esta imagem também pode ser publicada em um repositório central(Registry) e pode ser compartilhada em ambiente de desenvolvimento ou produção para utilização:
A seguir iremos criar o arquivo Docker Compose na solução que vai permitir orquestrar os contêineres da aplicação e do banco de dados SQL Server que iremos usar.
Podemos fazer todo esse processo manualmente mas podemos usar a integração do Visual Studio 2019 com o Docker Desktop for Windows e automatizar a maior parte dessa tarefa onde teremos a criação dos arquivos Dockerfile e docker-compose no projeto. A seguir basta somente fazer os ajustes necessários finais.
Vamos usar esta opção clicando com o botão direito do mouse sobre o nome do projeto e a seguir acionando o menu Add que vai abrir a seguinte janela com as opções:
1- Container
Orchestrator Support
2- Docker Support
Vamos clicar na primeira opção pois queremos gerar o Dockerfile e o arquivo docker-compose;
Na próxima janela selecione - Docker Compose - e clique em OK
Na sequência na opção Target OS escolha a opção Linux e clique em OK:
Ao final deste processo veremos na janela Solution Explorer os arquivos Dockerfile, docker-compose e .dockerignore criados:
Abaixo temos o código gerado para o arquivo Dockerfile que vai criar a imagem da aplicação :
FROM mcr.microsoft.com/dotnet/aspnet:5.0
AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build WORKDIR /src COPY ["EFCoreSqlServer/EFCoreSqlServer.csproj", "EFCoreSqlServer/"] RUN dotnet restore "EFCoreSqlServer/EFCoreSqlServer.csproj" COPY . . WORKDIR "/src/EFCoreSqlServer" RUN dotnet build "EFCoreSqlServer.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "EFCoreSqlServer.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "EFCoreSqlServer.dll"] |
Este arquivo não precisará sofrer nenhuma alteração e vai criar uma imagem para o nosso projeto ASP .NET Core.
A seguir temos o arquivo docker-compose onde incluímos o serviço - sqlserver - com as seguintes definições para criar o contêiner do SQL Server :
version: '3.4'
services:
EFCoreSqlServer:
image: ${DOCKER_REGISTRY}productswebapi
build:
context: .
dockerfile: EFCoreSqlServer/Dockerfile
depends_on:
- sqlserver
sqlserver:
image: microsoft/mssql-server-linux:2017-latest
hostname: 'sqlserver'
environment:
ACCEPT_EULA: 'Y'
SA_PASSWORD: "Numsey#2021"
volumes:
- c:\dados\volumes\mssql:/var/opt/mssql3
ports:
- '11433:1433'
expose:
- 1433
|
Temos também o arquivo docker-compose.override.yml com o código abaixo:
version: '3.4'
services:
EFCoreSqlServer:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "32033:80"
|
Neste código estamos mapeando a porta 32033 do host para a porta 80 do contêiner e definindo o ambiente como ambiente de desenvolvimento.
O
docker-compose.override.yml é o arquivo de
configuração onde você pode substituir as configurações existentes de
docker-compose.yml ou até mesmo adicionar serviços
completamente novos.
Por padrão, este arquivo não existe e você deve criá-lo manualmente. Como usamos
os recursos integrados do VS 2019 ele criou o arquivo automaticamente.
Agora para concluir vamos selecionar o item docker-compose na janela solution explorer e teclar ALT+ENTER.
Isso abrirá a janela de propriedades do docker-compose. Aqui altere a opção Service URL conforme abaixo:
Assim ao iniciar a nossa aplicação a URL irá acionar : http://localhost:XXXX/api/produtos
Agora temos tudo pronto para executar o nosso projeto em um contêiner e usar a API para fazer o CRUD em produtos acessando o SQL Server.
Executando o projeto iremos visualizar a janela Containers no VS 2019 exibindo o contêiner da aplicação e do SQL Server em execução:
Podemos abrir o Dashboard do Docker Desktop no Windows e visualizar os contêineres em execução:
Para acessar a aplicação digite : localhost:32033/api/products no navegador. Isso irá exibir a seguinte pagina :
Nada será exibido pois não populamos a tabela Products no SQL Server.
Vamos então na próxima parte do artigo usar o Postman para realizar as operações CRUD em nosso Contêiner.
Pegue o projeto aqui : EFCoreDocker.zip (sem as referências)
"Os que confiam no SENHOR serão como
o monte de Sião, que não se abala, mas permanece para sempre."
Salmos 125:1
Referências:
C# - Tasks x Threads. Qual a diferença
VB .NET - Datas, horas: conceitos e operações
C# - Programação Assíncrona como : Asycn e Task
O tratamento de datas no VB.NET
C# - Obtendo a data e a hora por TimeZone
Docker - Uma introdução básica -
Docker - Criando um Contâiner para .NET Core ...
Docker - Trabalhando com contêineres
Motivos para usar Docker com .NET Core -
Docker - MiniCurso Básico
Dockerfile - O processo de criação e build
Docker - Criando um Contâiner para .NET Core (3.0)