O MongoDB é um banco de dados não relacional, também chamado de NoSQL. É orientado a documentos, os quais podem ser descritos com dados no formato chave-valor utilizando o formato JSON (JavaScript Object Notation).
Os bancos de dados NoSQL, diferente dos bancos relacionais, não utilizam os esquemas de organização por meio de tabelas, linhas e colunas. E apresentam algumas vantagens como escalabilidade, flexibilidade, bom desempenho e facilidade para consultas.
Driver MongoDB C#
Neste exemplo criaremos uma API Web que fará as operações de CRUD (criar, ler, atualizar e deletar) no banco de dados NoSQL MongoDB.
Modelo de configuração
[Estação de trabalho] Instalar o MongoDB utilizando Docker: MongoDb Docker
[Código] Adicionar a dependência do pacote NuGet do MongoDB: MongoDB.Driver.
[Código] Inicialmente adicionaremos os valores de conexão do banco de dados no arquivo de configurações denominando
appsettings.json. Passando informações como a string de conexão (ConnectionString), o banco de dados (DatabaseName) e a coleção (CollectionName) que iremos acessar no MongoDB.
appsettings.json
{ "MongoDatabase": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "BookStore", "CollectionName": "Books" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" } - [Código] Agora criaremos uma classe denominada
DatabaseSettingsque irá conter os parâmetros equivalentes aos que criamos noappsettings.json. Esta classe irá armazenar os valores de propriedade doappsettings.json.
public class DatabaseSettings { public string ConnectionString { get; set; } = null!; public string DatabaseName { get; set; } = null!; public string CollectionName { get; set; } = null!; } - [Código] No
Program.csadicionaremos a linha a seguir. A instância de configuração da seçãoMongoDatabasepresente no arquivoappsettings.jsoné registrada no contêiner de injeção de dependência.
... builder.Services.Configure<DatabaseSettings>( builder.Configuration.GetSection("MongoDatabase")); Em outras palavras, a propriedade ConnectionString de um objeto do tipo DatabaseSettings será populada com a propriedade MongoDatabase:ConnectionString do appsettings.json.
Modelo de entidade
- [Código] A seguir criaremos nosso modelo de entidade que utilizaremos em todas as operações do CRUD.
public class Book { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string? Id { get; set; } [BsonElement("Name")] public string BookName { get; set; } = null!; public decimal Price { get; set; } public string Category { get; set; } = null!; public string Author { get; set; } = null!; } Descrição das annotations:
[BsonId]: Designa a propriedade Id como chave primária.
[BsonRepresentation(BsonType.ObjectId)]: Permite a passagem do parâmetro como tipo string em vez de uma estrutura ObjectId. O MongoDB processa a conversão de string para ObjectId.
[BsonElement("Name")]: O valor Namedo atributo [BsonElement] representa o nome da propriedade da coleção do MongoDB.
Adicionando o serviço de operações CRUD
- [Código]: Criar uma classe de serviço denominada
BooksService.
public class BooksService { private readonly IMongoCollection<Book> _booksCollection; public BooksService(IOptions<DatabaseSettings> bookStoreDatabaseSettings) { var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.CollectionName); } ... No código acima temos nossa classe BooksService e seu construtor. Uma instância do DatabaseSettings é recuperada da injeção de dependência pela injeção do construtor. Utilizamos a interface IOptions pois já está registrado como Singleton e pode ser injetado em qualquer vida útil do serviço.
Em cenários onde as opções devem ser recalculadas a cada solicitação, podemos utilizar o
IOptionsSnapshot. Sendo este registrado como Scoped.
Esta técnica fornece acesso para os valores de configuração do appsettings.json, que foi configurado no início deste artigo.
- [Código]: Agora no Program adicionaremos a linha subsequente.
... builder.Services.AddSingleton<BooksService>(); Neste trecho a classe BooksService é registrada com a injeção de dependência para dar suporte à injeção de construtor nas classes consumidoras.
O tempo de vida Singleton é o mais apropriado pois a classe
BooksServiceutiliza uma dependência direta deMongoCliente de acordo com as diretrizes oficiais do MongoDB, recomenda-se armazenar uma instância doMongoClientcom o tipo de vida útil Singleton.Singleton significa que um objeto do serviço é criado e fornecido para todas as requisições. Assim, todas as requisições obtém o mesmo objeto.
Voltando à classe BooksService, utilizamos os seguintes membros do MongoDB.Driver.
var mongoClient = new MongoClient( bookStoreDatabaseSettings.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase( bookStoreDatabaseSettings.Value.DatabaseName); _booksCollection = mongoDatabase.GetCollection<Book>( bookStoreDatabaseSettings.Value.CollectionName); MongoClient lê a instância do servidor para executar operações de banco de dados e espera uma string de conexão como parâmetro.
Após o MongoClient se conectar a uma instância do MongoDB, o método GetDatabase é utilizado para acessar um banco de dados.
Já o método GetCollection é utilizado para acessar uma coleção. Na chamada para o GetCollection<TDocument>(collection) temos o TDocument sendo o tipo de objeto armazenado na coleção e o collection representando o nome da coleção.
- [Código]: Adicionaremos os seguintes métodos na nossa classe
BooksService.
public class BooksService { ... 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); } Os seguintes métodos são acessados nesta classe:
Find: retorna todos os documentos na coleção que correspondem aos critérios de pesquisa fornecidos.
InsertOneAsync: insere o objeto fornecido como um novo documento na coleção.
ReplaceOneAsync: substitui o único documento que corresponde aos critérios de pesquisa fornecidos com o objeto fornecido.
DeleteOneAsync: exclui um único documento que corresponde aos critérios de pesquisa fornecidos.
Outros métodos que podem ser utilizados estão listados no link IMongoCollectionExtensions Methods.
Adicionar um controller
- [Código]: No nosso último passo iremos adicionar um controlador utilizando o seguinte código.
Neste trecho temos métodos de ação GET, POST, PUT e DELETE, que darão suporte às nossas solicitações.
[ApiController] [Route("api/[controller]")] public class BooksController : ControllerBase { private readonly BooksService _booksService; public BooksController(BooksService booksService) => _booksService = booksService; [ProducesResponseType(404)] [ProducesResponseType(200)] [HttpGet] public async Task<List<Book>> Get() => await _booksService.GetAsync(); [ProducesResponseType(404)] [ProducesResponseType(200)] [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; } [ProducesResponseType(404)] [ProducesResponseType(200)] [HttpPost] public async Task<IActionResult> Post(Book newBook) { await _booksService.CreateAsync(newBook); return CreatedAtAction(nameof(Get), new { id = newBook.Id }, newBook); } [ProducesResponseType(404)] [ProducesResponseType(200)] [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; await _booksService.UpdateAsync(id, updatedBook); return NoContent(); } [ProducesResponseType(404)] [ProducesResponseType(200)] [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(); } } Após isso, iremos adicionar a chamada do método AddControllers e para o método MapControllers no nosso Program
... builder.Services.AddControllers() .AddJsonOptions( options => options.JsonSerializerOptions.PropertyNamingPolicy = null); ... app.MapControllers(); Com a alteração
AddJsonOptionsanterior, os nomes de propriedade na resposta JSON serializada da API Web correspondem aos respectivos nomes de propriedade no tipo de objeto.
Ferramentas utilizadas
- Pacote Nuget: MongoDB.Driver
- Linguagem: C# .NET 8.0
Código no GitHub
Com isso finalizamos nossos entendimentos iniciais acerca de MongoDB e sobre como configurá-lo de maneira adequada. Além de termos bastante aparato teórico para entender muito sobre o nosso código. O código final está disponível no GitHub e seu link está disponível acima. Abraços!
Referências
Treinaweb - O que é MongoDB?
Microsoft Learn - Criar uma API Web com o ASP.NET Core e o MongoDB
MongoDB - Connection String
MongoDB - Databases and Collections
MongoDB - IMongoCollectionExtensions Methods
Top comments (0)