Usando o LINQ para consultar o MongoDB em um aplicativo .NET Core
Avalie esse Tutorial
Se você está acompanhando minha série de tutoriais sobre .NET Core e MongoDB, provavelmente se lembrará que exploramos usando o operador Localizar para executar query de documentos, bem como um pipeline de agregação. Nenhum desses assuntos explorados anteriormente é muito difícil, mas, dependendo do que você está tentando realizar, eles podem ser um pouco confusos. Sem mencionar que eles não são necessariamente "o modo .NET" de fazer negócios.
É aqui que o LINQ entra na jogada!
Com queries integradas à linguagem (LINQ), podemos usar uma sintaxe C# estabelecida e conhecida para trabalhar com nossos documentos e dados do MongoDB.
Neste tutorial, veremos algumas queries LINQ, algumas como substitutas de queries simples usando a API de query do MongoDB e outras como substitutas de pipelines de agregação mais complicados.
Para ser bem-sucedido com este tutorial, você já deve ter o seguinte pronto para uso:
- .NET Core 6+
- MongoDB Atlas, a camada gratuita ou superior
Quando se trata do MongoDB Atlas, você precisará ter um cluster implantado e configurado corretamente com funções de usuário e regras de rede. Se precisar de ajuda com isso, dê uma olhada no meu tutorial anterior sobre o assunto. Você também precisará dos conjuntos de dados de amostra instalados.
Embora este tutorial faça parte de uma série, você não precisa ter lido os outros para ter sucesso. No entanto, seria interessante verificar as outras maneiras de fazer negócios com o .NET Core e o MongoDB.
Para manter este tutorial simples e fácil de entender, vamos criar um novo aplicativo de console e trabalhar a partir dele.
Execute o seguinte na CLI para criar um novo projeto que esteja pronto para ser usado com o driver do MongoDB:
1 dotnet new console -o MongoExample 2 cd MongoExample 3 dotnet add package MongoDB.Driver
Neste tutorial, nossa string URI do MongoDB Atlas será armazenada como uma variável de ambiente em nosso computador. Dependendo do seu sistema operacional, você pode fazer algo assim:
1 export ATLAS_URI="YOUR_ATLAS_URI_HERE"
A string URI do Atlas pode ser encontrada em seu MongoDB Atlas Dashboard depois de clicar no botão "Connect" e escolher sua linguagem de programação.
Abra o arquivo Program.cs do projeto e adicione o seguinte código C#:
1 using MongoDB.Bson; 2 using MongoDB.Driver; 3 using MongoDB.Driver.Linq; 4 5 MongoClientSettings settings = MongoClientSettings.FromConnectionString( 6 Environment.GetEnvironmentVariable("ATLAS_URI") 7 ); 8 9 settings.LinqProvider = LinqProvider.V3; 10 11 MongoClient client = new MongoClient(settings);
No código acima, estamos dizendo explicitamente que queremos usar a versão 3 do LINQ em vez da versão 2, que é o padrão no MongoDB. Embora você possa realizar muitas tarefas relacionadas ao LINQ no MongoDB com a versão 2, você terá uma experiência muito melhor com a versão 3.
Vamos desacelerar e trabalhar para queries maiores e mais complicadas com o LINQ.
Caso você nunca tenha visto o banco de dados "sample_mflix" que faz parte dos conjuntos de dados de amostra que o MongoDB oferece, ele é um banco de dados de filmes com várias coleções. Vamos nos concentrar estritamente na coleção "filmes", que tem documentos semelhantes a este:
1 { 2 "_id": ObjectId("573a1398f29313caabceb515"), 3 "title": "Batman", 4 "year": 1989, 5 "rated": "PG-13", 6 "runtime": 126, 7 "plot": "The Dark Knight of Gotham City begins his war on crime with his first major enemy being the clownishly homicidal Joker.", 8 "cast": [ "Michael Keaton", "Jack Nicholson", "Kim Basinger" ] 9 }
Há muitos outros campos em cada documento da coleção, mas os campos acima são suficientes para nos ajudar.
Para usar o LINQ, precisaremos criar classes mapeadas para nossa coleção. Em outras palavras, não queremos usar
BsonDocument
ao escrever nossas queries. Na raiz do seu projeto, crie um arquivo Movie.cs com o seguinte código C#:1 using MongoDB.Bson; 2 using MongoDB.Bson.Serialization.Attributes; 3 4 [ ]5 public class Movie { 6 7 [ ]8 [ ]9 public string Id { get; set; } 10 11 [ ]12 public string Title { get; set; } = null!; 13 14 [ ]15 public int Year { get; set; } 16 17 [ ]18 public int Runtime { get; set; } 19 20 [ ]21 [ ]22 public string Plot { get; set; } = null!; 23 24 [ ]25 [ ]26 public List<string> Cast { get; set; } = null!; 27 28 }
Usamos uma classe como a que vimos acima em nossos tutoriais anteriores. Acabamos de definir alguns de nossos campos, mapeá-los para campos BSON em nosso banco de dados e dizer à nossa classe para ignorar quaisquer campos extras que possam existir em nosso banco de dados que optamos por não definir em nossa classe.
Digamos que queremos retornar os filmes lançados entre 1980 e 1990. Se não tivéssemos usado LINQ, estaríamos fazendo algo como o seguinte em nosso arquivo Program.cs:
1 using MongoDB.Bson; 2 using MongoDB.Driver; 3 4 MongoClient client = new MongoClient( 5 Environment.GetEnvironmentVariable("ATLAS_URI") 6 ); 7 8 IMongoCollection<Movie> moviesCollection = client.GetDatabase("sample_mflix").GetCollection<Movie>("movies"); 9 10 BsonDocument filter = new BsonDocument{ 11 { 12 "year", new BsonDocument{ 13 { "$gt", 1980 }, 14 { "$lt", 1990 } 15 } 16 } 17 }; 18 19 List<Movie> movies = moviesCollection.Find(filter).ToList(); 20 21 foreach(Movie movie in movies) { 22 Console.WriteLine($"{movie.Title}: {movie.Plot}"); 23 }
No entanto, como queremos usar o LINQ, podemos atualizar nosso arquivo Program.cs para que fique parecido com o seguinte:
1 using MongoDB.Driver; 2 using MongoDB.Driver.Linq; 3 4 MongoClientSettings settings = MongoClientSettings.FromConnectionString( 5 Environment.GetEnvironmentVariable("ATLAS_URI") 6 ); 7 8 settings.LinqProvider = LinqProvider.V3; 9 10 MongoClient client = new MongoClient(settings); 11 12 IMongoCollection<Movie> moviesCollection = client.GetDatabase("sample_mflix").GetCollection<Movie>("movies"); 13 14 IMongoQueryable<Movie> results = 15 from movie in moviesCollection.AsQueryable() 16 where movie.Year > 1980 && movie.Year < 1990 17 select movie; 18 19 foreach(Movie result in results) { 20 Console.WriteLine("{0}: {1}", result.Title, result.Plot); 21 }
No código acima, estamos obtendo uma referência à nossa coleção e criando uma query LINQ. Para detalhar a query LINQ para ver como ela se relaciona com MongoDB, temos o seguinte:
- O operador "WHERE" é o equivalente a fazer um "$MATCH" ou um filtro no MongoDB. Os documentos devem corresponder aos critérios dessa etapa.
- O operador "SELECT" é equivalente a fazer uma projeção ou usar o operador "$PROJECT". Estamos definindo quais campos devem ser retornados da query—neste caso, todos os campos que definimos em nossa classe.
Para diversificar um pouco nosso exemplo, vamos alterar a condição de correspondência para corresponder dentro de um array, algo não plano.
Altere a query LINQ para ficar assim:
1 var results = 2 from movie in moviesCollection.AsQueryable() 3 where movie.Cast.Contains("Michael Keaton") 4 select new { movie.Title, movie.Plot };
Algumas coisas mudaram no código acima junto com o filtro. Primeiro, você notará que estamos fazendo a correspondência na array
Cast
, desde que "Michael Keaton" exista nessa array. Em seguida, você notará que estamos fazendo uma projeção para retornar apenas o título e o enredo do filme em vez de todos os outros campos que possam existir nos dados.Vamos tornar as coisas um pouco mais complexas agora em termos de query. Desta vez, vamos fazer o que seria um pipeline de agregação do MongoDB, mas, desta vez, usando LINQ.
Altere o código C# no arquivo Program.cs para que fique parecido com o seguinte:
1 using MongoDB.Driver; 2 using MongoDB.Driver.Linq; 3 4 MongoClientSettings settings = MongoClientSettings.FromConnectionString( 5 Environment.GetEnvironmentVariable("ATLAS_URI") 6 ); 7 8 settings.LinqProvider = LinqProvider.V3; 9 10 MongoClient client = new MongoClient(settings); 11 12 IMongoCollection<Movie> moviesCollection = client.GetDatabase("sample_mflix").GetCollection<Movie>("movies"); 13 14 var results = 15 from movie in moviesCollection.AsQueryable() 16 where movie.Cast.Contains("Ryan Reynolds") 17 from cast in movie.Cast 18 where cast == "Ryan Reynolds" 19 group movie by cast into g 20 select new { Cast = g.Key, Sum = g.Sum(x => x.Runtime) }; 21 22 foreach(var result in results) { 23 Console.WriteLine("{0} appeared on screen for {1} minutes!", result.Cast, result.Sum); 24 }
Na query LINQ acima, estamos realizando uma série de etapas, como estágios em um pipeline de agregação. Esses estágios podem ser divididos da seguinte forma:
- Combine todos os documentos onde "Ryan Reynolds" está no elenco.
- Desenrole o array de nós do elenco para que os documentos fiquem adjacentes uns aos outros. Isso vai nivelar o array para nós.
- Faça outra correspondência no subconjunto de documentos, agora menor, filtrando apenas os resultados que contenham "Ryan Reynolds" neles.
- Agrupe os resultados restantes pelo elenco, que será apenas "Ryan Reynolds" neste exemplo.
- Projete apenas a chave do grupo, que é o membro do elenco, e a soma de todos os tempos de execução do filme.
Se você ainda não compreendeu, o que tentamos fazer foi determinar a quantidade total de tempo de tela que Ryan Reynolds teve. Isolamos nosso conjunto de resultados apenas para documentos com Ryan Reynolds e, em seguida, somamos o tempo de execução dos documentos que foram combinados.
Embora o escopo completo do pipeline de agregação MongoDB não seja compatível com LINQ, você poderá realizar muitas coisas, resultando em um código de aparência muito mais limpa. Para ter uma ideia dos operadores compatíveis, consulte a documentação do MongoDB LINQ.
Você acabou de experimentar o LINQ com MongoDB em seus aplicativos .NET Core. Embora você não precise usar o LINQ, conforme demonstrado em alguns tutoriais anteriores, é uma prática comum entre os desenvolvedores C#.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.