Consulte seus dados com o ASP.NET Core, o OData e o provedor do MongoDB Entity Framework Core
Avalie esse Tutorial
Entre as boas notícias do MongoDB.local NYC '24, estava o anúncio de que o MongoDB Entity Framework Core Provider ficara disponível globalmente. O fornecedor se integra ao Entity Framework e habilita desenvolvedores .NET a acessar facilmente dados em bancos de dados MongoDB com um framework bem conhecido.
Esta publicação mostra como você pode configurar o provedor em um projeto C# e criar uma API com o ASP.NET Core que oferece um ponto de extremidade compatível com OData. Para quem é novo no OData, esse é um protocolo usado para criar APIs REST consultáveis. As interfaces de usuário podem acessar essas APIs e usá-las para fornecer visualizações tabulares que oferecem um conjunto avançado de interações com o usuário, como filtragem, classificação ou paginação. Como o OData é um padrão aberto, há uma grande variedade de ferramentas que podem acessar endpoints compatíveis com OData, por exemplo, Microsoft Excel ou Microsoft Power BI.
Se você quiser acompanhar as amostras nesta publicação, criamos um endpoint que consulta os filmes no sample_flix-database. A maneira mais fácil de obter uma instância deste banco de dados de exemplo é carregar os dados de exemplo em um cluster MongoDB Atlas. Se você ainda não tiver um cluster, poderá configurar um cluster gratuito do MongoDB Atlas M0 (consulte este link para obter um guia).
Embora esta publicação use um projeto recém-criado de API Web do ASP.NET Core, é perfeitamente possível adicionar OData para oferecer os endpoints além dos endpoints REST existentes. Ao criar um novo projeto, talvez você queira ativar o suporte OpenAPI para ter uma maneira fácil de testar a API mais tarde. Além disso, o novo projeto deve usar controladores em vez de uma API mínima.
Após configurar o projeto ASP.NET Core, primeiro definiremos o Entity Framework DbContext e, em seguida, o configuraremos com o MongoDB Entity Framework Core Provider.
A primeira etapa é adicionar o pacote MongoDB Entity Framework Core Provider ao projeto por meio da UI ou da CLI
dotnet
:1 dotnet add package MongoDB.EntityFrameworkCore
Em seguida, definimos a classe de entidade que contém as propriedades que queremos ler do banco de dados:
1 // requires using MongoDB.Bson; 2 public class Movie 3 { 4 public string Id { get; set; } = ObjectId.GenerateNewId().ToString(); 5 public string Title { get; set; } = string.Empty; 6 public string? Plot { get; set; } 7 public IEnumerable<string>? Genres { get; set; } 8 public IEnumerable<string>? Cast { get; set; } 9 // additional properties as required 10 }
Algumas das propriedades (por exemplo
Plot
) são tipos de referência anuláveis. Isso afirma que valores ausentes no banco de dados não resultam em erro ao ler os documentos.A próxima etapa é definir o DbContext e adicionar a configuração para a entidade. Isso inclui vincular a entidade a uma coleção e, como estamos trabalhando com documentos existentes que seguem uma convenção de nomenclatura diferente, mapear os nomes de propriedade do documento para os do .NET POCO:
1 // requires using Microsoft.EntityFrameworkCore; 2 // requires using MongoDB.Bson; 3 // requires using MongoDB.EntityFrameworkCore.Extensions; 4 5 public class MflixDbContext : DbContext 6 { 7 public MflixDbContext(DbContextOptions options) 8 : base(options) 9 { 10 } 11 12 public DbSet<Movie> Movies { get; init; } 13 14 protected override void OnModelCreating(ModelBuilder modelBuilder) 15 { 16 base.OnModelCreating(modelBuilder); 17 var movieEntity = modelBuilder.Entity<Movie>(); 18 movieEntity.ToCollection("movies"); 19 movieEntity.Property(x => x.Id) 20 .HasElementName("_id") 21 .HasConversion<ObjectId>(); 22 movieEntity.Property(x => x.Title).HasElementName("title"); 23 movieEntity.Property(x => x.Plot).HasElementName("plot"); 24 movieEntity.Property(x => x.Genres).HasElementName("genres"); 25 movieEntity.Property(x => x.Cast).HasElementName("cast"); 26 } 27 }
A propriedade
Id
requer atenção especial: no documento MongoDB, a propriedade_id
é armazenada como um ObjectId. Em uma interface OData, usar strings em vez de um ObjectId
como valor do identificador é muito mais fácil. Ao adicionar conversão de valor, podemos ter o melhor dos dois mundos: ObjectIds no MongoDB e strings no .NET.Para finalizar a configuração do MongoDB Entity Framework Core Provider, registramos o cliente, o banco de dados e o
MflixDbContext
no container IoC:1 // requires using Microsoft.EntityFrameworkCore; 2 // requires using MongoDB.Driver; 3 // requires using MongoDBEFODataVerify; 4 5 builder.Services.AddSingleton<IMongoClient>(prov => 6 { 7 // Retrieve connection string from configuration 8 return new MongoClient("<your MongoDB connection string>"); 9 }); 10 builder.Services.AddSingleton(prov => 11 { 12 var client = prov.GetRequiredService<IMongoClient>(); 13 // Retrieve database name from configuration 14 return client.GetDatabase("sample_mflix"); 15 }); 16 builder.Services.AddDbContext<MflixDbContext>((prov, options) => 17 { 18 var database = prov.GetRequiredService<IMongoDatabase>(); 19 options.UseMongoDB( 20 database.Client, 21 database.DatabaseNamespace.DatabaseName); 22 });
Se você estiver usando apenas o Entity Framework, será suficiente registrar
MflixDbContext
e criar o cliente no método factory. O código de exemplo acima registra o cliente e a instância do banco de dados separadamente para que você possa injetá-los independentemente, se necessário.Com a configuração
DbContext
, também poderíamos usar o scaffolding do controlador para gerar o código para os controladores REST, se necessário. No entanto, para o propósito desta postagem, criaremos um endpoint OData do zero.A primeira etapa ao adicionar OData a um projeto é fazer referência ao pacote
Microsoft.AspNetCore.OData
, por meio da interface do usuário ou usando a CLIdotnet
:1 dotnet add package Microsoft.AspNetCore.OData
Em seguida, adicionamos um método que define o modelo EDM para a interface OData em
Program.cs
:1 // requires using Microsoft.OData.Edm; 2 // requires using Microsoft.OData.ModelBuilder; 3 4 IEdmModel GetEdmModel() 5 { 6 var model = new ODataConventionModelBuilder(); 7 model.EnableLowerCamelCase(); 8 model.EntitySet<Movie>("Movies"); 9 return model.GetEdmModel(); 10 }
EnableLowerCamelCase
altera a convenção de nomenclatura das propriedades para começar com um caractere minúsculo. Isso corresponde ao comportamento da serialização JSON em uma REST API padrão.Em seguida, registramos os componentes do OData no contêiner IoC adicionando o seguinte código em
Program.cs
:1 // requires using Microsoft.OData; 2 3 // This should be already in the code 4 builder.Services 5 .AddControllers() 6 // This is new 7 .AddOData(options => options 8 .Select() 9 .Filter() 10 .OrderBy() 11 .Count() 12 .SetMaxTop(100) 13 .AddRouteComponents("odata", GetEdmModel()));
O código acima permite várias operações do OData e restringe o número máximo de entidades que um cliente pode solicitar para evitar um conjunto de resultados muito grande.
Por último, adicionamos um controlador que fornece o ponto de extremidade OData para nossa query. O nome do controlador deve ser
MoviesController
para corresponder ao registro da entidade definida em GetEdmModel
:1 // requires using Microsoft.AspNetCore.OData.Query; 2 // requires using Microsoft.AspNetCore.OData.Routing.Controllers; 3 4 public class MoviesController : ODataController 5 { 6 private readonly MflixDbContext _dbContext; 7 8 public MoviesController(MflixDbContext dbContext) 9 { 10 _dbContext = dbContext; 11 } 12 13 [ 14 15 ] 16 public IQueryable<Movie> Get() 17 { 18 return _dbContext.Movies; 19 } 20 }
Injetamos uma instância de
MflixDbContext
no controlador para recuperar os filmes e marcar o métodoGet
com o atributoEnableQuery
. A propriedadePageSize
nos permite definir o número máximo de resultados enviados ao cliente. O uso deAllowedOrderByProperties
restringe as propriedades que podem ser usadas em operações de classificação.O nome deve corresponder ao nome serializado - camel case minúsculas em nossa amostra, conforme chamamos
EnableLowerCamelCase
em GetEdmModel
anteriormente. Existem várias outras opções que você pode usar para ajustar o comportamento do endpoint de query. Consulte a documentação do ASP.NET Core OData para obter detalhes.Agora estamos prontos para testar o endpoint OData e iniciá-lo no depurador. Se você habilitou o OpenAPI e o Swagger UI em seu projeto, você pode usar o Swagger UI para um teste simples. Isto é suficiente para verificar a prontidão básica do endpoint e serve como um bom ponto de partida.
Para solicitações mais sofisticadas, insira a URL do OData em um navegador ou use uma ferramenta como o Postman ou
curl
com URLs do OData.Uma solicitação simples para
https://localhost:\<YOUR PORT\>/odata/Movies
deve retornar 10 filmes no seguinte formato:1 { 2 "@odata.context": "https://localhost:7104/odata/$metadata#Movies", 3 "value": [ 4 { 5 "id": "573a1390f29313caabcd4135", 6 "title": "Blacksmith Scene", 7 "plot": "Three men hammer on an anvil...", 8 "genres": [ 9 "Short" 10 ], 11 "cast": [ 12 "Charles Kayser", 13 "John Ott" 14 ] 15 }, 16 // 999 more results 17 ], 18 "@odata.nextLink": "https://localhost:7104/odata/Movies?$skip=10" 19 }
Para usar os recursos do OData, como paginação e filtragem, você também pode tentar as seguintes solicitações:
- Filtrar por título:
https://localhost:\<YOUR PORT\>/odata/Movies?$filter=title eq 'The Godfather'
- Filtrar por parte do título - não diferencia maiúsculas de minúsculas:
https://localhost:\<YOUR PORT\>/odata/Movies?$filter=contains(tolower(title), 'godfather')
- Paginação com
$skip
,$top
e$count
:https://localhost:\<YOUR PORT\>/odata/Movies?$count=true&$top=10&$skip=10
- Classificando com
$orderBy
:https://localhost:\<YOUR PORT\>/odata/Movies?$orderBy=title ASC
E se você já tiver uma API e estiver usando o driver MongoDB C# sem Entity Framework? Puro e simples, tudo que você precisa para que o OData funcione é um
IQueryable<T>
.Isso pode ser fornecido facilmente por
IMongoCollection<T>.AsQueryable()
para que também possamos criar o endpoint OData dessa forma:1 // requires using Microsoft.AspNetCore.OData.Query; 2 // requires using Microsoft.AspNetCore.OData.Routing.Controllers; 3 // requires using MongoDB.Driver; 4 5 public class MoviesController : ODataController 6 { 7 private readonly IMongoCollection<Movie> _collMovies; 8 9 public MoviesController(IMongoDatabase db) 10 { 11 _collMovies = db.GetCollection<Movie>("movies"); 12 } 13 14 [ 15 16 ]17 public IQueryable<Movie> Get() 18 { 19 return _collMovies.AsQueryable(); 20 } 21 }
Observe que você também precisa configurar o mapeamento para a classe
Movie
adicionando atributos ou definindo o mapa de classes obrigatóriamente.Até agora, criamos um ponto de extremidade OData que oferece suporte a opções básicas para filtragem, classificação e paginação com o pacote Microsoft.AspNetCore.OData. Infelizmente, este pacote não suporta
$select
e $expand
em suas solicitações. Se você quiser dar um passo além e também adicionar suporte para essas operações, precisará confiar em um pacote fornecido pelo MongoDB e fazer referência aMongoDB.AspNetCore.OData em vez do pacote da Microsoft. Tudo que você precisa fazer é substituir EnableQuery
por MongoEnableQuery
e seu endpoint suportará essas operações.1 // requires using Microsoft.AspNetCore.OData.Routing.Controllers; 2 // requires using MongoDB.AspNetCore.OData.Query; 3 // requires using MongoDB.Driver; 4 5 public class MoviesController : ODataController 6 { 7 private readonly IMongoCollection<Movie> _collMovies; 8 9 public MoviesController(IMongoDatabase db) 10 { 11 _collMovies = db.GetCollection<Movie>("movies"); 12 } 13 14 // Change this from EnableQuery to MongoEnableQuery 15 [ 16 17 ]18 public IQueryable<Movie> Get() 19 { 20 return _collMovies.AsQueryable(); 21 } 22 }
No momento da redação deste artigo, você precisa seguir a abordagem descrita na seção OData usando o driver C# do MongoDB acima e não pode usar um
DbSet<T>
fornecido pelo MongoDB Entity Framework Core Provider.Nesta publicação, mostramos como é fácil adicionar opções de query avançadas à sua API, com um endpoint OData que se baseia no Entity Framework e no MongoDB. Essa API pode ser usada para fornecer opções de query avançadas na interface do usuário de seus aplicativos ou pode ser acessada a partir de ferramentas populares como o Microsoft Excel. Como ambas as estruturas são muito poderosas e oferecem muitas opções, não deixe de conferir a documentação no MongoDB Entity Framework Core Provider e ASP.NET Core OData.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.