Este é o projeto tem como objetivo central a criação de uma api que conte com end-point de get all para pessoas, a entidade contém os dados de nome, idade e gênero, além de um id único em formato GUID.
Sua aplicação deve ter:
-
Um end-point para cada um dos métodos get, put, post e delete.
-
Deve ser possÃvel criar um registro de livro
-
Deve ser possÃvel editar um registro de livro
-
Deve ser possÃvel deletar um registro de livro
- usar o id na rota
-
Deve ser possÃvel buscar a lista de livros já catalogados
-
Um livro deve ter:
- TÃtulo
- Autor
- Descrição
- preço
- Ed - edição (opcional)
- gênero literário
-
Descrição e edição devem ser campo opicionais. E os demais obrigatórios
-
É preciso usar um banco de dados mongoDB
- deve ter um id único gerado para o mongoDB
É obrigatório a utilização de:
- .net
- Criar campo de data de criação que não pode ser alterado
- criar um campo de data de alteração que é alterado a cada update
- o valor inicial na criação é o mesmo valor da data de criação
- configurar o máximo de descrição para 511 caracteres
- definir o valor mÃnimo do tÃtulo para 10 caracteres e o máximo em 127
Para fazer o bong funionar é adicionado um driver o mongoDB.
Então é criado uma model que representa as configurações para conexão com a cluster. Essa model traz representação de string de conexão, o nome da base e o nome da coleção.
namespace BookStoreApi.Models;
public class BookStoreDatabaseSettings
{
public string ConnectionString { get; set; } = null!;
public string DatabaseName { get; set; } = null!;
public string BooksCollectionName { get; set; } = null!;
}
Então é adicionado n appseettings.json o seguuinte bloco:
"BookStoreDatabase": {
"ConnectionString": "mongodb+srv://nome:senha@cluster0.jhwjo5u.mongodb.net/",
"DatabaseName": "BookStore",
"BooksCollectionName": "Books"
},
Esse bloco contém as dfinições da classe de configuração usada como model.
Após isso é preciso definir essa base no Program.cs, oo que é feito adicionando as seguintes linhas.
builder.Services.Configure<BookStoreDatabaseSettings>(
builder.Configuration.GetSection("BookStoreDatabase"));
Para usar de fato esse banco é criado um modelo da entidade que vamos armazenar e manipular, que é um livro. Essa classe estará na pasta de models.
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace BookStoreApi.Models;
public class Book
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
[BsonElement("Title")]
public string Title { get; set; } = null!;
public decimal Price { get; set; }
public string? Description { get; set; } = null!;
public string Category { get; set; } = null!;
public string Author { get; set; } = null!;
public int? Edition { get; set; } = null!;
public DateTime? Created_time { get; } = DateTime.Now!;
public DateTime? Updated_time { get; set; } = DateTime.Now!;
}
Quando há a marcação da interrogação após o tipo de dado é que ele é opcional, e os que não tem é devido a ser um dado obrigatório. Para casos comoo id, Created_time e updated_time é porque não se espera esses dados do body quando se cria, mas eles são inseridos pela controller.
E para casos de deixar um valor de fallback é usado a notação abaixo, sendo que estiver entre o = e a ! é o valor de fallback
= null!
A nossa controller não difere muito do modelo de uso normal, ou usado com um context db.
É importado a model e a service de book necessárias na parte superior do arquivo.
using BookStoreApi.Models;
using BookStoreApi.Services;
Então see cria a instância da service no construtor da classe.
private readonly BooksService _booksService;
public BookController(BooksService booksService) =>
_booksService = booksService;
No demais se usa os métodos com notação async/await e sempre usando a instância da srvice com o métodos presentes na service. E para valores não esperados por rotas como o post e put são atribuÃdos dentro do método.
using BookStoreApi.Models;
using BookStoreApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace books_api.Controllers
{
[Route("/api/books")]
[ApiController]
public class BookController : Controller
{
private readonly BooksService _booksService;
public BookController(BooksService booksService) =>
_booksService = booksService;
[HttpGet]
public async Task<List<Book>> Get() =>
await _booksService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<Book>> Get(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
return NotFound();
return book;
}
[HttpPost]
public async Task<IActionResult> Post(Book newBook)
{
await _booksService.CreateAsync(newBook);
return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook);
}
[HttpPut("{id:length(24)}")]
public async Task<IActionResult> Update(string id, Book updatedBook)
{
var book = await _booksService.GetAsync(id);
if (book is null)
return NotFound();
updatedBook.Id = book.Id;
updatedBook.updated_time = DateTime.Now;
await _booksService.UpdateAsync(id, updatedBook);
return NoContent();
}
[HttpDelete("{id:length(24)}")]
public async Task<IActionResult> Delete(string id)
{
var book = await _booksService.GetAsync(id);
if (book is null)
return NotFound();
await _booksService.RemoveAsync(id);
return NoContent();
}
}
}
Como melhoria podia se validar melhor dados vindos para os métodos, para não aceitar nada fora do formato exato esperado.
A service é construÃda usando o construtor para adicionar as propriedades do mongo, para executar como um client do mesmo. Cada método executa alguma lógica de manipulação dos dados.
É interessante salientar o comportamento de sobrecarga de métodos do c# aqui, que tem dois getAsync, sendo um que não recebe parâmetros e outro que recebe. Isso já torna o service capaz de identificar qual método chamar em cada caso.
using BookStoreApi.Models;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace BookStoreApi.Services;
public class BooksService
{
private readonly IMongoCollection<Book> _booksCollection;
public BooksService(
IOptions<BookStoreDatabaseSettings> bookStoreDatabaseSettings)
{
var mongoClient = new MongoClient(
bookStoreDatabaseSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
bookStoreDatabaseSettings.Value.DatabaseName);
_booksCollection = mongoDatabase.GetCollection<Book>(
bookStoreDatabaseSettings.Value.BooksCollectionName);
}
public async Task<List<Book>> GetAsync() =>
await _booksCollection.Find(_ => true).ToListAsync();
public async Task<Book?> GetAsync(string id) =>
await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
public async Task CreateAsync(Book newBook) =>
await _booksCollection.InsertOneAsync(newBook);
public async Task UpdateAsync(string id, Book updatedBook) =>
await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
public async Task RemoveAsync(string id) =>
await _booksCollection.DeleteOneAsync(x => x.Id == id);
}
Para a service poder ser usada adequadamente é preciso declarar ela no Program.cs como mostrado abaixo.
builder.Services.AddSingleton<BooksService>();
Conectar um banco mongo de forma simples no c# com asp.net é bem simpes, já que a doc do .net fornece exemplos muito bons. Apesar disso não é tão simples encontrar alguns exemplos de uso, na doc ou em vÃdeos de tutoriais. Como por exemplo, definir o dado de forma mais completa na model, tal como quantidade mÃnima e máxima de caracteres de um campo, o que não sei se é devido a prática não ser usada no .net ou se a informação não é tão fácil de localizar.
A service interage bem com a controller, o que é uma diferença para o context db que não tinha service.
Para dados mais complexos e tratamentos mais elaborados pode ser mais complicado de achar exemplos concretos.
- C#
- .net 8
- visual studio
- mongoDB
- Linkedin - jeanmeira
- Instagram - @jean.meira10
- GitHub - JCDMeira