Asp.Net Core com MongoDb e Mongo Atlas - C#
Este tutorial cria uma API Web que executa as operações CRUD (criar, ler, atualizar e excluir) em um banco de dados NoSQL do MongoDB.
Baseado no artigo Microsoft Docs, criado por Pratik Khandelwal e Scott Addie
Modificado por Ricardo Mauro
Tempo de leitura: 18 minutos
Neste tutorial, você aprenderá como:
- Configurar o Mongo Atlas e MongoDb
- Definir usuário e senha de acesso no Mongo Atlas
- Executar operações CRUD do MongoDB a partir de uma API Web Asp.Net Core 3
Pré-requisitos
- SDK do .NET Core 3.0 ou posterior
- Visual Studio Code
- C# para Visual Studio Code
- C# Extensions para Visual Studio Code
Configurando o MongoDB na nuvem Mongo Atlas
Acessar o Mongo Atlas --https://www.mongodb.com/cloud/atlas.
Clique em Try free
caso você não possua conta, ou Sign In
se você já possui uma conta no Mongo Atlas.
Criando uma conta para acesso ao Mongo Atlas
Preencha os dados básicos (email, first name, last name e password).
E ative a conta pelo e-mail recebido (Welcome to MongoDB) clicando em Sign in
.
Sign In
Entre com seu usuário e senha.
Preparando o ambiente
O cluster é onde será armazenado os banco de dados Mongo Db (podemos criar mais de um). No nosso caso iremos utilizar o banco criado por padrão quando criamos o primeiro cluster.
-
Clicar em
Build a Cluster
. -
Selecionar o cluster free (
Starter Clusters
) e depois clicar emCreate a cluster
. -
Em Cloud Provider selecionar
Azure
e deixar selecionada região pré-selecionadaVirginia (eastus2)
. -
Para as demais opções vamos deixar os valores padrões.
-
Agora clique em
Create Cluster
e aguarde o termino da criação do cluster.
A barra superior azul indica o status da criação do cluster. Pode demorar um pouco até o termino.
Um pouco de contexto - O que vemos aqui?
Do lado esquerdo temos as opções possíveis de configuração, como Database Access e Network Access.
-
Clusters
é onde vemos dados gerais sobre o cluster e é onde iremos obter a string de conexão. -
Database Access
é onde gerenciamos usuário(s) e senha(s) para acesso a(s) base(s) de dados. -
Network Access
é onde gerenciamos regras de firewall (liberamos IP). -
Demais opções deixo para vocês explorarem depois.
Preparando o ambiente para nossa aplicação
1 - Criando o primeiro usuário
-
Clicar em
Database Access
. -
Depois clicar em
ADD NEW USER
do lado direito da tela (botão verde). -
Inserir os seguintes dados:
> Username = alunos
> Password = alunos
> User privileges = read and write to any database
Por último clicar em Add User
.
A criação do usuário pode tomar algum tempo.
2 - Criando regra no firewall
-
Primeiro clicar em
Network Access
. -
Depois em
ADD IP ADDRESS
. -
Agora em
ALLOW ACCESS FROM ANYWHERE
(isso permite acesso de qualquer IP)
Observe que utilizaremos isso para facilidade de acesso, porém não é recomendado para ambientes de produção. -
Por fim clicar em
Confirm
.
Essa mudança também pode levar algum tempo para surtir efeito.
3 - Coletando string de conexão (connectionString)
-
Clicar em
Clusters
no menu lateral esquerdo. -
Depois clicar no botão
CONNECT
na sessãoSANDBOX
. -
No popup aberto clicar em
Connect your application
.
-
Na sessão
Driver
selecionarC# / .NET
e emversion
selecionar2.5 or later
. -
Copiar a
connectionString
para o notepad e substituir o<password>
pela senha que criamos (alunos) e substituir também<usuario>
caso já não esteja preenchido.
Resultado deverá ser algo como:
mongodb+srv://alunos:[email protected]/test?retryWrites=true&w=majority
Ótimo! Temos o nosso banco de dados pronto para nossa aplicação. Já podemos começar a criação da API Web com ASP.NET Core.
Criando o projeto da Web API do ASP.NET Core
-
Primeiro crie uma pasta vazia chamada MongoWebApi dentro da pasta Documentos. Este será nosso local de trabalho (ou workspace em inglês), neste pasta será depositado todos os nossos código-fonte csharp, arquivos de configuração, compilados (nossas dll's) e etc.
-
Agora abra o Visual Code e abra a nossa pasta de trabalho (Arquivo - Abrir pasta... ou se inglês File - Open Folder...) que acabamos de criar.
-
Abra o terminal utilizando o menu superior Terminal depois Novo Terminal ou New Terminal em inglês.
-
No terminal aberto digite
dotnet new webapi -o AlunosApi
Um novo projeto de API Web do ASP.NET Core será criado na pasta AlunosAPI dentro do Visual Code.
Caso o apareça uma caixa de diálogo perguntando se Os ativos necessários para criar e depurar estão ausentes do 'AlunosAPI'. Deseja adicioná-los? . Selecione Sim na barra superior.
- Novamente no mesmo Terminal Integrado execute os seguintes comandos para instalar o driver .NET para MongoDB:
cd AlunosAPI
dotnet add AlunosApi.csproj package MongoDB.Driver
Isso irá adicionar as DLL's do MongoDb ao nosso projeto.
Removendo conteúdo desnecessário ao nosso projeto
Remova os arquivos abaixo.
- WeatherForecast.cs
- Controllers/WeatherForecastController.cs
Adicionando o modelo de entidade ao nosso projeto
Primeiro adicione uma pasta chamada Models à raiz do projeto.
Botão direito na pasta AlunosAPI e depois Nova pasta ou New folder com nome Models (atenção as letras maiúsculas e minúsculas).
Depois crie uma classe com nome Aluno
ao diretório Models. Botão direito na pasta Models depois New C# Class
.
Por último substitua o conteúdo do arquivo criado pelo conteúdo abaixo.
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace AlunosApi.Models
{
public class Aluno
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("Name")]
public string Nome { get; set; }
public string Email { get; set; }
}
}
Na classe anterior, a propriedade Id
:
- É necessária para mapear o objeto CLR (Common Language Runtime) para a coleção do MongoDB.
- É anotada com [BsonId] para ser designada como a chave primária do documento.
- É anotada com [BsonRepresentation(BsonType.ObjectId)] para permitir a passagem do parâmetro como o tipo
string
em vez de uma estrutura ObjectId. O Mongo processa a conversão destring
paraObjectId
.
A propriedade Nome
é anotada com o atributo [BsonElement]. O valor do atributo de Name
representa o nome da propriedade da coleção do MongoDB.
Adicionando o modelo de configuração
Primeiro substitua o conteúdo do arquivo appsettings.json pelo abaixo:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AlunosDatabaseSettings": {
"AlunosCollectionName": "Alunos",
"ConnectionString": "{VALOR COPIADO NO NOTEPAD}",
"DatabaseName": "AlunosDb"
}
}
Não esqueca de substituir o {VALOR COPIADO NO NOTEPAD} pelo valor real.
Agora crie o arquivo AlunosDatabaseSettings.cs no diretório Models com o código a seguir:
namespace AlunosApi.Models
{
public class AlunosDatabaseSettings : IDatabaseSettings
{
public string AlunosCollectionName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
public interface IDatabaseSettings
{
string AlunosCollectionName { get; set; }
string ConnectionString { get; set; }
string DatabaseName { get; set; }
}
}
A classe AlunosDatabaseSettings
é usada para armazenar os valores de propriedade AlunosDatabaseSettings
do arquivo appsettings.json. Os nomes de propriedade JSON e C# são nomeados de forma idêntica para facilitar o processo de mapeamento.
A seguir adicione o código ao Startup.ConfigureServices
(arquivo Startup.cs na raiz do projeto):
public void ConfigureServices(IServiceCollection services)
{
// requires using Microsoft.Extensions.Options
services.Configure<AlunosDatabaseSettings>(Configuration.GetSection(nameof(AlunosDatabaseSettings)));
services.AddSingleton<IDatabaseSettings>(sp =>sp.GetRequiredService<IOptions<AlunosDatabaseSettings>>().Value);
//... código pré-existente (não apagar)
}
Explicando o código anterior:
- A instância de configuração à qual a seção
AlunosDatabaseSettings
do arquivo appsettings.json é associada é registrada no contêiner de DI (Injeção de Dependência). Por exemplo, a propriedadeConnectionString
de um objetoAlunosDatabaseSettings
é populada com a propriedadeAlunosDatabaseSettings:ConnectionString
no appsettings.json. - A interface
IDatabaseSettings
é registrada na DI com um tempo de vida do serviço singleton. Quando inserida, a instância da interface é resolvida para um objetoAlunosDatabaseSettings
.
Agora adicione o seguinte trecho de código na parte superior do Startup.cs para resolver as referências AlunosDatabaseSettings
e IDatabaseSettings
:
using AlunosApi.Models;
using Microsoft.Extensions.Options;
Agora execute o comando no terminal integrado dotnet build
para validar o código produzido até agora.
A saída do comando deve ser algo como:
> dotnet build
Build succeeded.
0 Warning(s)
0 Error(s)
Caso possua Erro(s) reviso o código até aqui.
Estrutura do projeto até aqui
Adicionando um serviço de operações CRUD
- Cria o diretório Services abaixo da pasta AlunosApi (raiz do projeto).
- Adicione uma classe chamada
AlunosService
ao diretório Services recem criado com o seguinte código:
using AlunosApi.Models;
using MongoDB.Driver;
using System.Collections.Generic;
using System.Linq;
namespace AlunosApi.Services
{
public class AlunosService
{
private readonly IMongoCollection<Aluno> _alunos;
public AlunosService(IDatabaseSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
_alunos = database.GetCollection<Aluno>(settings.AlunosCollectionName);
}
public List<Aluno> Get() => _alunos.Find(aluno => true).ToList();
public Aluno Get(string id) => _alunos.Find<Aluno>(aluno => aluno.Id == id).FirstOrDefault();
public Aluno Create(Aluno aluno)
{
_alunos.InsertOne(aluno);
return aluno;
}
public void Update(string id, Aluno alunoIn) => _alunos.ReplaceOne(aluno => aluno.Id == id, alunoIn);
public void Remove(Aluno alunoIn) => _alunos.DeleteOne(aluno => aluno.Id == alunoIn.Id);
public void Remove(string id) => _alunos.DeleteOne(aluno => aluno.Id == id);
}
}
No código anterior, uma instância IDatabaseSettings
é recuperada da DI por meio da injeção de construtor. Essa técnica fornece acesso para os valores de configuração do appsettings.json que foram adicionados na seção Adicionando o modelo de configuração.
- Agora adicione o código a seguir a
Startup.ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
//--código pré-existente (não apagar)
//adicionar antes de services.AddControllers();
services.AddSingleton<AlunosService>();
//--código pré-existente (não apagar)
}
No código anterior, a classe AlunosService
é registrada com a DI para dar suporte à injeção de construtor nas classes consumidoras. O tempo de vida do serviço singleton é mais apropriado porque AlunosService
usa uma dependência direta de MongoClient
. De acordo com as Diretrizes oficiais de reutilização do cliente Mongo, o MongoClient
deve ser registrado na DI com um tempo de vida do serviço singleton.
- Não se esqueça de adicionar o seguinte código na parte superior do Startup.cs para resolver a referências
AlunosService
:
using AlunosApi.Services;
Rode o novamente comando dotnet build
no Terminal para verificarmos o estado do nosso código até aqui.
Saída deve ser algo como:
> dotnet build
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:05.22
A classe AlunosService
usa os seguintes membros MongoDB.Driver
para executar operações CRUD em relação ao banco de dados:
- MongoClient – lê a instância do servidor para executar operações de banco de dados. O construtor dessa classe é fornecido na cadeia de conexão do MongoDB:
public AlunosService(IDatabaseSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
_alunos = database.GetCollection<Aluno>(settings.AlunosCollectionName);
}
-
IMongoDatabase – representa o banco de dados Mongo para execução de operações. Este tutorial usa o método GetCollection<\TDocument>(coleção) genérico na interface para obter acesso aos dados em uma coleção específica. Execute operações CRUD em relação à coleção depois que esse método for chamado. Na chamada de método
GetCollection<TDocument>(collection)
:collection
representa o nome da coleção.TDocument
representa o tipo de objeto CLR armazenado na coleção.
GetCollection<TDocument>(collection)
retorna um objeto MongoCollection que representa a coleção. Neste tutorial, os seguintes métodos são invocados na coleção:
- DeleteOne – exclui um único documento que correspondem aos critérios de pesquisa fornecidos.
- Find<\TDocument> – retorna todos os documentos na coleção que correspondem aos critérios de pesquisa fornecidos.
- InsertOne – insere o objeto fornecido como um novo documento na coleção.
- ReplaceOne – substitui o único documento que corresponde aos critérios de pesquisa fornecidos com o objeto fornecido.
Adicionando um controlador (controller)
Adicione uma classe AlunosController
ao diretório Controllers (caso pasta não exista crie uma na raiz do projeto) com o seguinte código:
using AlunosApi.Models;
using AlunosApi.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace AlunosApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AlunosController : Controller
{
private readonly AlunosService _alunosService;
public AlunosController(AlunosService alunosService)
{
_alunosService = alunosService;
}
[HttpGet]
public ActionResult<List<Aluno>> Get() => _alunosService.Get();
[HttpGet("{id:length(24)}", Name = "GetAluno")]
public ActionResult<Aluno> Get(string id)
{
var aluno = _alunosService.Get(id);
if (aluno == null)
return NotFound();
return aluno;
}
[HttpPost]
public ActionResult<Aluno> Create(Aluno aluno)
{
_alunosService.Create(aluno);
return CreatedAtRoute("GetAluno", new { id = aluno.Id.ToString() }, aluno);
}
[HttpPut("{id:length(24)}")]
public IActionResult Update(string id, Aluno alunoIn)
{
var aluno = _alunosService.Get(id);
if (aluno == null)
return NotFound();
_alunosService.Update(id, alunoIn);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public IActionResult Delete(string id)
{
var aluno = _alunosService.Get(id);
if (aluno == null)
return NotFound();
_alunosService.Remove(aluno.Id);
return NoContent();
}
}
}
O controlador da API Web:
- Usa a classe
AlunosService
para executar operações CRUD. - Contém métodos de ação para dar suporte a solicitações GET, POST, PUT e DELETE HTTP.
- Chama o xref:System.Web.Http.ApiController.CreatedAtRoute* no método de ação
Create
para retornar uma resposta HTTP 201. O código de status 201 é a resposta padrão para um método HTTP POST que cria um recurso no servidor.CreatedAtRoute
também adiciona um cabeçalhoLocation
à resposta. O cabeçalhoLocation
especifica o URI do livro recém-criado.
Estrutura do projeto até aqui
Testando a API Web
Primeiro compile e execute o nosso aplicativo. Para isso clique em Debug
na barra superior do Visual Code e depois Start Debugging
.
Isso demorar um pouco na primeira executação. Você pode acompanhar o status do compilação através do terminal.
Caso seja sua primeira vez executando um aplicativo .Net no Visual Code, lhe será apresentado a seguinte janela.
Selecione a opção .NET Core. Isso irá fazer o Visual Code gerar os arquivos de configuração necessários para depurar a aplicação.
Execute novamente Debug
e Start Debugging
.
Quando pronto uma janela de navegador deverá abrir automaticamente com o endereço https://localhost:5001.
Na primeira execução pode lhe ser alertado sobre um certificado digital não confiável. Você pode aceitar o risco do certificado digital. Isso ocorre devido ao certificado digital de tempo de desenvolvimento (não confiável em ambiente produtio) do Asp.Net Core (utilizado somente em tempo Desenvolvimento). Por isso podemos confiar.
Agora navegue até https://localhost:5001/api/alunos
para testar o método de ação GET
sem parâmetros. A seguinte resposta JSON deve ser exibida:
[]
Perfeito! Esse retorno de array vazio é esperado pois não temos registros no Mongo Db (ainda !!!).
Isso é ótimo, conseguimos validar os seguintes items:
- Controller Alunos
- Leitura do Arquivo de Configuração
- Acesso ao Mongo Db através do Mongo Atlas
Executando testes mais elaborados
Para testes mais elaborados vamos utilizar a ferramenta Insomnia. Faça o download e instale-o caso você não o tenha.
Você também pode utilizar outras ferramentas, como POSTMan ou ARC. Neste caso decidi pelo Insomia.
Salve o conteúdo abaixo com o nome alunos-insomia.yaml. Este arquivo contém os testes de GET / POST / PUT e DELETE (deixei pronto para facilitar).
_type: export
__export_format: 4
__export_date: 2019-10-20T23:22:17.814Z
__export_source: insomnia.desktop.app:v7.0.3
resources:
- _id: req_005b4fcbbf534bd68589fb228859c347
authentication: {}
body: {}
created: 1571613715982
description: ""
headers: []
isPrivate: false
metaSortKey: -1571613582546.125
method: DELETE
modified: 1571613715982
name: Delete Aluno
parameters: []
parentId: fld_fa1061f900604e57a83ac239a9dae413
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingSendCookies: true
settingStoreCookies: true
url: https://localhost:5001/api/alunos/5daceb3f53f8fc3450886919
_type: request
- _id: fld_fa1061f900604e57a83ac239a9dae413
created: 1571613715912
description: ""
environment: {}
environmentPropertyOrder: null
metaSortKey: -1571613617577
modified: 1571613716019
name: Alunos
parentId: wrk_832b63dceba24667bbd70ddf633362fb
_type: request_group
- _id: wrk_832b63dceba24667bbd70ddf633362fb
created: 1571527078826
description: ""
modified: 1571527078825
name: Insomnia
parentId: null
_type: workspace
- _id: req_fc5d374d60b342e0bb59434c4b810f2a
authentication: {}
body: {}
created: 1571613715997
description: ""
headers: []
isPrivate: false
metaSortKey: -1571613582496.125
method: GET
modified: 1571613715997
name: Get Aluno Por Id
parameters: []
parentId: fld_fa1061f900604e57a83ac239a9dae413
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingSendCookies: true
settingStoreCookies: true
url: https://localhost:5001/api/alunos/5daceb3f53f8fc3450886919
_type: request
- _id: req_2320b4b3a6b54b01a2fa61c313e041a9
authentication: {}
body:
mimeType: application/json
text: |-
{
"id": "5daceb3f53f8fc3450886919",
"nome": "Aluno 1 - Update",
"email": "[email protected]"
}
created: 1571613715964
description: ""
headers:
- id: pair_a670675f0243435a9d3445694c7b58cb
name: Content-Type
value: application/json
isPrivate: false
metaSortKey: -1571613582446.125
method: PUT
modified: 1571613715964
name: Update Aluno
parameters: []
parentId: fld_fa1061f900604e57a83ac239a9dae413
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingSendCookies: true
settingStoreCookies: true
url: https://localhost:5001/api/alunos/5daceb3f53f8fc3450886919
_type: request
- _id: req_5dc593023227433bac88c0f00ee9851d
authentication: {}
body:
mimeType: application/json
text: |-
{
"nome": "Aluno 1",
"email": "[email protected]"
}
created: 1571613715946
description: ""
headers:
- id: pair_d62363541a994b13b9566c92cd572b96
name: Content-Type
value: application/json
isPrivate: false
metaSortKey: -1571613582396.125
method: POST
modified: 1571613715946
name: Create Aluno
parameters: []
parentId: fld_fa1061f900604e57a83ac239a9dae413
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingSendCookies: true
settingStoreCookies: true
url: https://localhost:5001/api/alunos
_type: request
- _id: req_31184175d6b5455e86d2e9e0ec34e57e
authentication: {}
body: {}
created: 1571613715929
description: ""
headers: []
isPrivate: false
metaSortKey: -1571613582346.125
method: GET
modified: 1571613715929
name: Get Alunos
parameters: []
parentId: fld_fa1061f900604e57a83ac239a9dae413
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingSendCookies: true
settingStoreCookies: true
url: https://localhost:5001/api/alunos
_type: request
- _id: env_7f3527ea80965a06126f1429c010d91e789910d7
color: null
created: 1571527079133
data: {}
dataPropertyOrder: null
isPrivate: false
metaSortKey: 1571527079133
modified: 1571527079133
name: Base Environment
parentId: wrk_832b63dceba24667bbd70ddf633362fb
_type: environment
- _id: jar_7f3527ea80965a06126f1429c010d91e789910d7
cookies: []
created: 1571527079152
modified: 1571527079152
name: Default Jar
parentId: wrk_832b63dceba24667bbd70ddf633362fb
_type: cookie_jar
alunos-insomia.yaml
Perfeito, agora clique em Import from file e importe o arquivo que acabamos de gravar.
Faça o primeiro teste, clique no item Get Alunos, devemos obter o mesmo array vazio.
Novamente pode lhe ser alertado sobre SSL Validation. Por isso também vamos aceitar o alerto clicando em Disable SSL Validation, depois na sessão Network desmarcar o item Validate certificates.
Agora teste as demais opções, como Create Aluno e depois volte ao Get Alunos para ver o aluno criado.
Tente utilizar o Update Aluno (não se esqueça de atualizar na rota (URL) com o Id do aluno).
Próximas etapas
Para saber mais sobre a criação de APIs Web do ASP.NET Core, confira os seguintes recursos:
- Versão do artigo original no YouTube
- Artigo original: Microsoft Docs
- Inclua novos modelos como "Turma", "Curso", etc.