Introdução ao MongoDB Atlas e ao Azure Functions usando .NET e C#
Avalie esse Tutorial
Portanto, você precisa criar um aplicativo com custos operacionais mínimos que também possa ser dimensionado para atender à crescente demanda do seu negócio. Esse é um cenário perfeito para uma função sem servidor, como aquelas criadas com Azure Functions. Com as funções sem servidor, você pode se concentrar mais no aplicativo e menos no lado da infraestrutura e das operações. No entanto, o que acontece quando você precisa incluir um banco de dados na mistura?
Neste tutorial, exploraremos como criar uma função sem servidor com o Azure Functions e o tempo de execução do .NET para interagir com o MongoDB Atlas. Se você não estiver familiarizado com o MongoDB, ele oferece um modelo de dados flexível que pode ser usado para uma variedade de casos de uso, sendo integrado à maioria das pilhas de desenvolvimento de aplicativos com facilidade. É fácil dimensionar seu MongoDB database e as funções do Azure Functions para atender à demanda, tornando-os uma correspondência perfeita.
Existem alguns requisitos que devem ser atendidos antes de iniciar este tutorial:
- As ferramentas principais do Azure Functions instaladas e configuradas.
- .NET ou .NET Core 6.0+
- Um [MongoDB Atlas](https://www.mongodb.com/atlas banco de dados) implantado e configurado com regras de usuário e regras de rede apropriadas.
Usaremos a CLI do Azure para configurar o Azure e usaremos as Ferramentas Centrais do Azure Functions para criar e publicar funções sem servidor no Azure.
A configuração do MongoDB Atlas está fora do escopo deste tutorial, portanto, a suposição é que você tenha um banco de dados disponível, um usuário que possa acessar esse banco de dados e as regras de acesso à rede adequadas para que o Azure possa acessar seu banco de dados. Se precisar de ajuda para configurar esses itens, confira o tutorial do MongoDB Atlas para configurar tudo.
Vamos começar criando uma função do Azure localmente em nosso computador. Poderemos testar se tudo está a funcionar antes de carregá-lo no Azure.
Em um prompt de comando, execute o seguinte comando:
1 func init MongoExample
O comando acima iniciará o assistente para criar um novo projeto do Azure Functions. Quando solicitado, escolha .NET como o tempo de execução, pois nosso foco será o C#. Não importa se você escolhe o processo isolado ou não, mas não usaremos o processo isolado para este exemplo.
Com seu prompt de comando, navegue até o projeto recém-criado e execute o seguinte comando:
1 func new --name GetMovies --template "HTTP trigger"
O comando acima criará uma nova função "GetMovies" dentro do projeto usando o modelo "trigger HTTP", que é bastante básico. Na função "GetMovies", planejamos recuperar um ou mais filmes de nosso banco de dados.
Embora não fosse obrigatório usar o banco de dados de amostra MongoDB sample_mflix e filmes de coleção de amostra neste projeto, ele será referenciado por toda parte. Nada que façamos não possa ser replicado usando um banco de dados ou uma coleção personalizada.
Neste ponto, podemos começar a escrever algum código!
Como o MongoDB será um dos destaques deste tutorial, precisamos instalá-lo como uma dependência. Dentro do projeto, execute o seguinte no prompt de comando:
1 dotnet add package MongoDB.Driver
Se você estiver usando o NuGet, há comandos semelhantes que você pode usar, mas, para este exemplo, vamos nos limitar ao .NET CLI.
Como criamos uma nova função, devemos ter um arquivoGetMovies.cs na raiz do projeto. Abra-o e substitua o código existente pelo seguinte código C#:
1 using System; 2 using System.IO; 3 using System.Threading.Tasks; 4 using Microsoft.AspNetCore.Mvc; 5 using Microsoft.Azure.WebJobs; 6 using Microsoft.Azure.WebJobs.Extensions.Http; 7 using Microsoft.AspNetCore.Http; 8 using Microsoft.Extensions.Logging; 9 using Newtonsoft.Json; 10 using MongoDB.Driver; 11 using System.Collections.Generic; 12 using MongoDB.Bson.Serialization.Attributes; 13 using MongoDB.Bson; 14 using System.Text.Json.Serialization; 15 16 namespace MongoExample 17 { 18 19 [ ]20 public class Movie 21 { 22 23 [ ]24 [ ]25 public string? Id { get; set; } 26 27 [ ]28 [ ]29 public string Title { get; set; } = null!; 30 31 [ ]32 [ ]33 public string Plot { get; set; } = null!; 34 35 } 36 37 public static class GetMovies 38 { 39 40 public static Lazy<MongoClient> lazyClient = new Lazy<MongoClient>(InitializeMongoClient); 41 public static MongoClient client = lazyClient.Value; 42 43 public static MongoClient InitializeMongoClient() 44 { 45 return new MongoClient(Environment.GetEnvironmentVariable("MONGODB_ATLAS_URI")); 46 } 47 48 [ ]49 public static async Task<IActionResult> Run( 50 [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, 51 ILogger log) 52 { 53 54 string limit = req.Query["limit"]; 55 IMongoCollection<Movie> moviesCollection = client.GetDatabase("sample_mflix").GetCollection<Movie>("movies"); 56 57 BsonDocument filter = new BsonDocument{ 58 { 59 "year", new BsonDocument{ 60 { "$gt", 2005 }, 61 { "$lt", 2010 } 62 } 63 } 64 }; 65 66 var moviesToFind = moviesCollection.Find(filter); 67 68 if(limit != null && Int32.Parse(limit) > 0) { 69 moviesToFind.Limit(Int32.Parse(limit)); 70 } 71 72 List<Movie> movies = moviesToFind.ToList(); 73 74 return new OkObjectResult(movies); 75 76 } 77 78 } 79 80 }
Há muita coisa acontecendo no código acima, mas vamos detalhá-lo para que faça sentido.
Dentro do namespace, você notará que temos uma classeMovie :
1 [ ]2 public class Movie 3 { 4 5 [ ]6 [ ]7 public string? Id { get; set; } 8 9 [ ]10 [ ]11 public string Title { get; set; } = null!; 12 13 [ ]14 [ ]15 public string Plot { get; set; } = null!; 16 17 }
A classe acima destina-se a mapear nossos objetos C# locais para campos dentro de nossos documentos. Se você estiver usando o banco de dadossample_mflix e a coleção defilmes, esses são campos dessa coleção. A classe não representa todos os campos, mas como o [BsonIgnoreExtraElements] está incluído, isso não importa. Neste caso, somente os campos da classe atual serão utilizados.
Em seguida, você notará alguma lógica de inicialização para nosso banco de dados:
1 public static Lazy<MongoClient> lazyClient = new Lazy<MongoClient>(InitializeMongoClient); 2 public static MongoClient client = lazyClient.Value; 3 4 public static MongoClient InitializeMongoClient() 5 { 6 7 return new MongoClient(Environment.GetEnvironmentVariable("MONGODB_ATLAS_URI")); 8 9 }
Estamos usando a classeLazy para a inicialização preguiçosa da nossa conexão com o banco de dados. Isso é feito fora da função executável da nossa classe porque não é eficiente estabelecer conexões em cada execução da nossa Função do Azure. As conexões simultâneas com o MongoDB e praticamente todos os bancos de dados existentes são finitas, portanto, se você tiver uma Azure Function de grande escala, as coisas podem ficar ruins rapidamente se você estiver estabelecendo uma conexão todas as vezes. Em vez disso, estabelecemos conexões conforme necessário.
Observe a variável de ambienteMONGODB_ATLAS_URI. Obteremos esse valor em breve e garantiremos que ele seja exportado para o Azure.
Isso nos leva à lógica real da nossa função Azure:
1 string limit = req.Query["limit"]; 2 3 IMongoCollection<Movie> moviesCollection = client.GetDatabase("sample_mflix").GetCollection<Movie>("movies"); 4 5 BsonDocument filter = new BsonDocument{ 6 { 7 "year", new BsonDocument{ 8 { "$gt", 2005 }, 9 { "$lt", 2010 } 10 } 11 } 12 }; 13 14 var moviesToFind = moviesCollection.Find(filter); 15 16 if(limit != null && Int32.Parse(limit) > 0) { 17 moviesToFind.Limit(Int32.Parse(limit)); 18 } 19 20 List<Movie> movies = moviesToFind.ToList(); 21 22 return new OkObjectResult(movies);
No código acima, aceitamos umavariável limitada do cliente que executa a função. Não é um requisito e não precisa ser chamado de limite, mas faz sentido para nós.
Depois de obter uma referência ao banco de dados e à collection que desejamos usar, definimos o filtro para a consulta que desejamos executar. Neste exemplo, estamos tentando devolver apenas documentos para filmes que foram lançados entre os anos 2005 e 2010. Em seguida, usamos esse filtro na operaçãoLocalizar.
Como queremos limitar nossos resultados, verificamos se o limite existe e garantimos que ele tem um valor com o qual podemos trabalhar. Se atingir, usamos esse valor como nosso limite.
Finalmente, convertemos nosso conjunto de resultados em uma lista e a retornamos. Azure faz o resto por nós!
Quer testar esta função localmente antes de implementá-la? Primeiro, verifique se você tem sua string Atlas URI e defina-a como uma variável de ambiente em seu computador local. Isso pode ser obtido por meio do Painel do MongoDB Atlas.
O melhor lugar para adicionar sua variável de ambiente para o projeto é dentro do arquivolocal.settings.jsonda seguinte forma:
1 { 2 "IsEncrypted": false, 3 "Values": { 4 // OTHER VALUES ... 5 "MONGODB_ATLAS_URI": "mongodb+srv://<USER>:<PASS>@<CLUSTER>.170lwj0.mongodb.net/?retryWrites=true&w=majority" 6 }, 7 "ConnectionStrings": {} 8 }
O arquivolocal.settings.json não é enviado para o Azure, mas lidaremos com isso mais tarde.
Com a variável de ambiente definida, execute o seguinte comando:
1 func start
Se ele foi executado com sucesso, você receberá um URL para testar. Tente adicionar um limite e veja os resultados que ele retorna.
Neste ponto, podemos preparar o projeto a ser implantado no Azure.
Conforme mencionado anteriormente no tutorial, você deve ter o Azure CLI. Vamos usá-lo para fazer várias configurações no Azure.
Em um prompt de comando, execute o seguinte:
1 az group create --name <GROUP_NAME> --location <AZURE_REGION>
O comando acima criará um grupo. Certifique-se de dar um nome que faça sentido para você, assim como para uma região. O nome que você escolher para o grupo será usado nas próximas etapas.
Com o grupo criado, execute o seguinte comando para criar uma conta de armazenamento:
1 az storage account create --name <STORAGE_NAME> --location <AZURE_REGION> --resource-group <GROUP_NAME> --sku Standard_LRS
Ao criar a conta de armazenamento, use o mesmo grupo anterior e forneça novas informações, como um nome para o armazenamento e uma região. A conta de armazenamento será usada quando tentarmos implantar a função na nuvem do Azure.
A última coisa que precisamos criar é a função dentro do Azure. Execute o seguinte:
1 az functionapp create --resource-group <GROUP_NAME> --consumption-plan-location <AZURE_REGION> --runtime dotnet --functions-version 4 --name <APP_NAME> --storage-account <STORAGE_NAME>
Use as regiões, grupos e contas de armazenamento dos comandos anteriores ao criar sua função. No comando acima, estamos definindo o tempo de execução do .NET, um dos muitos tempos de execução possíveis que o Azure oferece. Na verdade, se você quiser ver como trabalhar com o MongoDB usando o Node.js, confira este tutorial sobre o tópico.
A maior parte da cloud Azure agora está configurada. Veremos a configuração final no final deste tutorial quando se trata de nossa variável de ambiente, mas por enquanto terminamos. No entanto, agora precisamos vincular o projeto local e o projeto cloud em preparação para a implementação.
Navegue até o seu projeto com um prompt de comando e execute o seguinte comando:
1 func azure functionapp fetch-app-settings <FUNCTION_APP_NAME>
O comando acima baixará as informações de configurações do Azure para seu projeto local. Apenas certifique-se de ter escolhido o nome da função correto nas etapas anteriores.
Também precisamos baixar as informações de armazenamento.
No prompt de comando, execute o seguinte comando:
1 func azure storage fetch-connection-string <STORAGE_ACCOUNT_NAME>
Depois de executar o comando acima, você terá as informações de armazenamento necessárias na nuvem do Azure.
Temos um projeto e esse projeto está vinculado ao Azure. Agora podemos nos concentrar nas etapas finais da implantação.
A primeira coisa que precisamos fazer é lidar com nossa variável de ambiente. Podemos fazer isso por meio da CLI ou da interface da web, mas por uma questão de rapidez, vamos usar a CLI.
No prompt de comando, execute o seguinte:
1 az functionapp config appsettings set --name <FUNCTION_APP_NAME> --resource-group <RESOURCE_GROUP_NAME> --settings MONGODB_ATLAS_URI=<MONGODB_ATLAS_URI>
A variável de ambiente que estamos enviando é a MONGODB_ATLAS_URI, como vimos anteriormente. Certifique-se de adicionar o valor correto, bem como as outras informações relacionadas no comando acima. Você precisaria fazer isso para cada variável de ambiente que criasse, mas, felizmente, este projeto tinha apenas uma.
Por fim, podemos fazer o seguinte:
1 func azure functionapp publish <FUNCTION_APP_NAME>
O comando acima publicará nossa função Azure. Quando terminar, ele fornecerá um link a partir do qual você poderá acessá-lo.
Não se lembre de obter uma "chave de host" do Azure antes de tentar acessar sua função a partir do cURL, do navegador da web ou semelhante, caso contrário, você provavelmente receberá uma resposta de erro não autorizada.
1 curl https://<FUNCTION_APP_NAME>.azurewebsites.net/api/GetMovies?code=<HOST_KEY_HERE>
O cURL acima é um exemplo do que você pode executar, basta trocar os valores para corresponder aos seus.
Você acabou de ver como criar uma função do Azure que se comunica com o MongoDB Atlas usando o tempo de execução do .NET. Este tutorial explorou vários tópicos, incluindo várias ferramentas CLI, conexões eficientes do banco de dados e a query de dados do MongoDB. Este tutorial pode ser facilmente estendido para realizar tarefas mais complexas no MongoDB, como usar pipelines de agregação e outras operações CRUD básicas.
Se você está procurando algo semelhante usando o runtime Node.js, confira este outro tutorial sobre o assunto.
Com o MongoDB Atlas no Microsoft Azure, os desenvolvedores recebem acesso à plataforma de dados para desenvolvedores mais abrangente, segura, escalável e baseada na nuvem do mercado. Agora, com a disponibilidade do Atlas no Azure Marketplace, nunca foi tão fácil para os usuários começarem a construir com o Atlas enquanto simplificam os processos de aquisição e cobrança. Comece hoje mesmo por meio da listagemdo Atlas no Azure Marketplace .