Documentando APIs ASP.NET com Swagger

O Swagger é uma poderosa ferramenta para documentar e testar APIs, tornando o processo de desenvolvimento mais ágil e eficiente. Quando integrado a projetos ASP.NET, ele não apenas automatiza a geração da documentação, mas também oferece uma interface gráfica interativa para explorar os endpoints da API em tempo real. Neste artigo, vamos detalhar como configurar o Swagger em projetos ASP.NET, personalizar sua documentação e facilitar a compreensão e uso da sua API por outros desenvolvedores. Adicionando o Swagger no ASP.NET Instalação do pacote No Visual Studio, ao configurar uma aplicação de API em .NET, a opção de adicionar suporte ao OpenAPI (Swagger) geralmente já vem habilitada por padrão. Porém, caso você tenha uma aplicação que não possua suporte ao Swagger e deseje implementá-lo, siga os seguintes passos: No seu projeto ASP.NET, você precisa adicionar o pacote NuGet “Swashbuckle.AspNetCore”. Vá até Ferramentas > Gerenciador de Pacotes NuGet > Console do Gerenciador de Pacotes e execute o seguinte comando: Install-Package Swashbuckle.AspNetCore Isso irá adicionar o pacote necessário ao seu projeto, permitindo que você configure e utilize o Swagger para documentar sua API. Configurando o Swagger Agora, você precisa configurar o Swagger para que ele funcione corretamente. No arquivo “Program.cs” (ou “Startup.cs”, dependendo da versão do ASP.NET Core), adicione o Swagger no pipeline de serviços: builder.Services.AddSwaggerGen(c => { c.SwaggerDoc(“v1”, new OpenApiInfo { Title = “API Documentação Swagger”, Description = “Esta é uma API de ensino sobre o swagger”, Contact = new OpenApiContact { Name = “Next Wave Education”, Email = “email@mail.com”, Url = new Uri(“https://nextwave.education/”) }, Version = “v1″ }); O “AddSwaggerGen” adiciona o serviço do Swagger no pipeline da aplicação. Dentro dessa função, o método “SwaggerDoc” é chamado para definir um documento que representará a API. Essas informações serão exibidas na interface gráfica do Swagger, facilitando a visualização e o uso da API pelos desenvolvedores. O resultado será semelhante a esse: Ainda no arquivo “Program.cs” vamos configurar o “Swagger” e o “Swagger UI” para serem usados em um ambiente de desenvolvimento, permitindo que a documentação da API seja acessível através de uma interface gráfica. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } O “app.UseSwagger()” ativa o middleware do Swagger, responsável por gerar um arquivo JSON contendo a documentação dos endpoints da API. Esse arquivo JSON segue o formato especificado pela OpenAPI e que vai ser utilizado pelo Swagger UI para exibir a documentação de forma legível e interativa. O “app.UseSwaggerUI()” é responsável por configurar a interface gráfica do Swagger UI, que é a página onde os desenvolvedores podem interagir com a API. Em nosso exemplo utilizamos a condição “if” para verificar se a aplicação está em ambiente de desenvolvimento. Se sim, deve ser apresentada a interface do swagger, caso contrário a interface não será apresentada. Em seguida, localize a pasta “Properties” no seu projeto. Dentro dessa pasta, você encontrará o arquivo “launchSettings.json”. Abra esse arquivo e altere o valor do parâmetro “launchUrl” para “swagger”. Isso fará com que o Swagger seja carregado automaticamente como a página inicial quando você executar a aplicação. Pronto! Agora a sua aplicação já está sendo executada com documentação feita pelo swagger. Agora vamos para a segunda parte do nosso artigo onde vamos enriquecer a nossa documentação com detalhes que ajudam os desenvolvedores a entenderem melhor como utilizar os endpoints. Customizando a documentação Para gerar uma documentação mais rica, vamos ativar a geração de comentários XML. Para isso, localize o arquivo do projeto e adicione a linha “ <GenerateDocumentationFile>true</GenerateDocumentationFile>” . Essa linha instrui o compilador a gerar um arquivo XML contendo todos os comentários documentados em seu código durante o processo de compilação. builder.Services.AddSwaggerGen(c => { //Configuração adicionada anteriormente da documentação do swagger. var xmlFile = $”{Assembly.GetExecutingAssembly().GetName().Name}.xml”; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); }); Nesta parte do código, estamos configurando o Swagger para incluir a documentação gerada a partir dos comentários XML. Primeiro, criamos o nome do arquivo XML com base no nome da assembly atual. Em seguida, usamos “Path.Combine” para gerar o caminho completo desse arquivo no diretório da aplicação. Por fim, chamamos “c.IncludeXmlComments(xmlPath)” para incluir esses comentários na documentação do Swagger, garantindo que as descrições e exemplos que você escreveu no código sejam exibidos na interface do Swagger UI. Agora na controller, acima dos métodos dos endpoints, é possível adicionar comentários para melhorar a documentação, da seguinte forma: // Comentários aqui [HttpGet] public ActionResult BuscarTodos() { } Summary /// /// Obtém todos os produtos. /// O <summary> é utilizado para fornecer uma descrição concisa do que o método faz. Neste caso, a explicação é: “Obtém todos os produtos”. Resultado na interface: Remarks /// /// Retorno: /// /// GET /Produtos /// { /// “id”: 1, /// “nome”: “Cadeira”, /// “valor”: 999 /// } /// Na seção <remarks>, podemos adicionar um exemplo do retorno esperado ou descrever como os dados devem ser enviados ou recebidos. No exemplo acima, mostramos a estrutura dos dados que serão retornados em formato JSON. Resultado na interface: Response /// Retorna uma lista de itens de Produtos quando a requisição tem sucesso. /// Se encontrar um erro. Os comentários <response> especificam os códigos de resposta que o método pode retornar, junto com uma breve descrição. Aqui usamos: 201 (Created): indica que a requisição foi bem-sucedida e retornou a lista de produtos. 400 (Bad Request): indica que houve um erro na requisição, por exemplo, devido a dados inválidos. Os atributos [ProducesResponseType] devem ser colocados logo abaixo do método http (ex: HttpGet) e servem para declarar os códigos de resposta suportados pelo endpoint. No exemplo abaixo, indicamos que o método pode retornar os status 201 Created e 400 Bad Request. [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] Resultado na interface: Configurando autenticação na UI do Swagger com JWT Uma funcionalidade importante para garantir segurança em suas APIs é a autenticação, e no caso de APIs que utilizam tokens JWT, o Swagger permite que você insira o token diretamente na interface para realizar testes nos endpoints protegidos. Vamos ver como configurar isso. Para configurar a autenticação JWT no Swagger, adicione o seguinte código
IHttpClientFactory: Otimizando o uso do HttpClient em .NET

Ao desenvolver aplicações .NET, é comum que você precise fazer requisições HTTP para consumir APIs externas. Embora o HttpClient seja uma ferramenta bastante robusta para esse propósito, ele também apresenta desafios como esgotamento de portas e comportamento inadequado em relação ao DNS quando mal utilizado. Neste artigo, vamos explorar maneiras de configurar e utilizar o HttpClient, focando nas práticas recomendadas para evitar problemas de performance e escalabilidade. Problema comum com HttpClient A abordagem mais simples para usar o HttpClient é criar uma nova instância sempre que for fazer uma requisição. var client = new HttpClient(); client.BaseAddress = new Uri(“https://jsonplaceholder.typicode.com”); var response = await client.GetAsync(“/posts”); Embora essa solução funcione, ela cria novos problemas. O ideal é que as instâncias do HttpClient sejam reutilizadas durante a vida útil da aplicação, pois criar novas instâncias constantemente pode causar esgotamento de portas. Esgotamento de portas (Socket Exhaustion) Cada vez que você instancia a classe HttpClient e faz uma requisição, é criada uma nova conexão de socket. No entanto, essas conexões não são imediatamente fechadas após a requisição terminar. Em vez disso, elas entram em um estado conhecido como TIME_WAIT, em que a conexão aguarda por um tempo antes de ser fechada completamente para garantir que pacotes de dados atrasados não sejam processados incorretamente. Como resultado, se você instanciar muitos “HttpClient” repetidamente sem reutilizá-los, o sistema acabará com uma quantidade enorme de conexões de rede pendentes nesse estado. Isso pode levar ao esgotamento de portas, quando o sistema não consegue abrir novas conexões de rede porque todas as portas disponíveis estão ocupadas pelas conexões abertas anteriormente. Uma prática recomendada ao usar HttpClient é reutilizar instâncias ao longo da vida útil da aplicação. Isso permite que as conexões subjacentes sejam mantidas abertas e reutilizadas para várias requisições, economizando tempo e evitando o problema de esgotamento de portas. No entanto, simplesmente reutilizar o HttpClient diretamente pode ter outros problemas, como manter conexões abertas indefinidamente, o que pode causar falhas se o servidor de destino mudar o endereço IP ou se houver alterações no DNS. Para gerenciar esse problema de forma eficiente, o HttpClient foi aprimorado nas versões mais recentes do .NET com o uso do HttpClientFactory que permite a criação e configuração de instâncias do HttpClient de forma eficiente, sem a necessidade de gerenciar manualmente o tempo de vida das instâncias. Utilizando IHttpClientFactory O primeiro passo para utilizar o IHttpClientFactory é registrar o serviço no contêiner de injeção de dependências. Isso normalmente é feito no arquivo “Program.cs”, onde você configura os serviços da sua aplicação: builder.Services.AddHttpClient(); Ao contrário da abordagem tradicional, em que uma nova instância de “HttpClient” é criada diretamente em cada requisição, com o “IHttpClientFactory” você pode solicitar um cliente HTTP sempre que precisar, e a fábrica gerenciará a criação e o descarte dessas instâncias de maneira otimizada. No exemplo abaixo, injetamos essa interface no construtor da classe service e a utilizamos a seguir, em um método que realiza uma requisição HTTP. private readonly IHttpClientFactory _httpClientFactory; public BlogService(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task BuscarPostsAsync() { var client = _httpClientFactory.CreateClient(); client.BaseAddress = new Uri(“https://jsonplaceholder.typicode.com”); var response = await client.GetAsync(“/posts”); return await response.Content.ReadAsStringAsync(); } Observe que agora, ao invés de utilizar o operador “new”, utilizamos o método “CreateClient()” para obter uma nova instância de HttpClient. Usando clientes nomeados ou tipados Uma outra possibilidade de configurar clientes HTTP é usar clientes nomeados e clientes tipados. public class BlogService { private readonly HttpClient _httpClient; public BlogService(HttpClient httpClient) { _httpClient = httpClient; } public async Task BuscarPostsAsync() { var response = await _httpClient.GetAsync(“/posts”); return await response.Content.ReadAsStringAsync(); } } Neste caso, a classe “BlogService” recebe uma instância do “HttpClient” configurado automaticamente. O método “ListarPostsAsync” utiliza a URL base que já foi definida na configuração. Assim, não é necessário passar um nome para criar uma nova instância do cliente, resultando em um código mais limpo e organizado. Observação: Tanto os clientes nomeados quanto os tipados devem ser configurados no “Program.cs”, após as outras injeções de dependências, para garantir que os serviços estejam disponíveis corretamente durante a execução da aplicação Clientes nomeados Os clientes nomeados são instâncias de HttpClient que são configuradas com base em um nome específico. Esse nome permite que você crie diferentes configurações para cada cliente de maneira personalizada e referenciável. Por exemplo, ao configurar um cliente nomeado para a API jsonplaceholder, o código poderia ser o seguinte: builder.Services.AddHttpClient(“JsonPlaceholder”, client => { client.BaseAddress = new Uri(“https://jsonplaceholder.typicode.com”); client.DefaultRequestHeaders.Add(“Accept”, “application/json”); }); Neste exemplo, o cliente nomeado “JsonPlaceholder” é configurado com a URL base e um cabeçalho padrão. public async Task BuscarPostsAsync() { var client = _httpClientFactory.CreateClient(“JsonPlaceholder”); var response = await client.GetAsync(“/posts”); return await response.Content.ReadAsStringAsync(); } O método “CreateClient(“JsonPlaceholder”)” busca o cliente com o nome específico “JsonPlaceholder” e retorna uma instância do HttpClient com todas as configurações que você definiu. Isso permite que você faça requisições a essa API de maneira simplificada e consistente. Essa abordagem não só reduz a duplicação de código, mas também facilita a manutenção e a escalabilidade da aplicação ao lidar com múltiplas APIs. Clientes tipados Os clientes tipados oferecem uma alternativa mais direta, onde o HttpClient é configurado e utilizado em uma classe específica. Diferente dos clientes nomeados, essa abordagem elimina a necessidade de fornecer um nome para o cliente. Em vez disso, o cliente é automaticamente associado à classe de serviço que o utiliza. builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri(“https://jsonplaceholder.typicode.com”); client.DefaultRequestHeaders.Add(“Accept”, “application/json”); }); A classe “BlogService” pode ser implementada da seguinte forma: Cuidados ao usar clientes tipados em serviços singleton Um ponto importante a ser observado é que clientes tipados são registrados com um tempo de vida transitório por padrão. Isso pode causar problemas se você tentar injetar um cliente tipado em um serviço singleton. O HttpClient ficará armazenado pelo tempo de vida do serviço singleton, o que pode levar a problemas de resolução de DNS. Para evitar isso, é recomendável configurar o tempo de vida das conexões utilizando o “SocketsHttpHandler”: services.AddHttpClient(client => { client.BaseAddress = new Uri(“https://jsonplaceholder.typicode.com”); client.DefaultRequestHeaders.Add(“Accept”, “application/json”); }) .ConfigurePrimaryHttpMessageHandler(() => { return new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(5)
Como realizar Bulk Update e Delete no Entity Framework

Em aplicações que lidam com grandes volumes de dados, a performance é um fator crucial para garantir eficiência. O Entity Framework (EF) nos oferece soluções para o gerenciamento de dados. No entanto, quando se trata de operações que envolvem a atualização ou exclusão de um grande número de registros, o comportamento padrão do EF pode ser ineficiente, pois normalmente ele realiza essas operações de maneira individual para cada entidade. Neste artigo vamos explorar estratégias para realizar bulk updates e bulk deletes com o Entity Framework, apresentando métodos nativos e o uso de bibliotecas externas que oferecem soluções otimizadas para cenários de alto volume de dados. Operações de atualização padrão O Entity Framework usa um ciclo de vida baseado no “DbContext” para rastrear as mudanças nas entidades e então enviá-las ao banco de dados. Quando você deseja atualizar várias entidades, o EF rastreia e executa as atualizações individualmente: using (var context = new ClientesDbContext()) { var clientes = context.Clientes.Where(c => c.Ativo).ToList(); foreach (var cliente in clientes) { cliente.Status = “Inativo”; } context.SaveChanges(); } Para cada cliente, o EF geraria um comando SQL semelhante a este: UPDATE Clientes SET Status = ‘Inativo’ WHERE Id = 1; Esse código funciona, mas para cada cliente atualizado, o EF enviará um comando SQL UPDATE ao banco de dados, o que pode ser muito custoso em termos de tempo. Bulk Update e Delete A partir do EF Core 7, está disponível o método “ExecuteUpdate”, que permite realizar atualizações em lote diretamente no banco de dados, sem a necessidade de carregar as entidades na memória. Sua sintaxe é a seguinte: context.Clientes .Where(c => c.Ativo) .ExecuteUpdate(c => c.SetProperty(c => c.Status, “Inativo”)); O Entity Framework vai gerar um único comando SQL semelhante a este: UPDATE Clientes SET Status = ‘Inativo’ WHERE Ativo = 1; Aqui, o método “ExecuteUpdate” aplica uma atualização em todos os registros que correspondem à consulta, enviando apenas uma única operação SQL para o banco de dados, o que melhora significativamente a performance. Para executar uma operação de exclusão em massa a estrutura é bem semelhante, bastando usar o método ExecuteDelete: context.Clientes .Where(c => c.Ativo) .ExecuteDelete(); Dessa vez, a operação executada deve ser equivalente a: DELETE FROM Clientes WHERE Ativo = 1 Alternativa: biblioteca “EFCore.BulkExtensions” Embora o EF Core 7 tenha melhorado significativamente o suporte para operações em lote, uma opção popular para versões anteriores ou para cenários mais avançados é usar bibliotecas como a “EFCore.BulkExtensions”. Esta biblioteca oferece suporte para bulk insert, bulk update e bulk delete com alta performance. .NET CLI dotnet add package EFCore.BulkExtensions Package Manager Install-Package EFCore.BulkExtensions Após instalar o pacote, podemos utilizá-lo da seguinte forma: var clientes = context.Clientes.Where(c => c.Ativo).ToList(); foreach (var cliente in clientes) { cliente.Status = “Inativo”; } context.BulkUpdate(clientes); Ao invés de chamar “SaveChanges” para salver as modificações realizadas no “cliente”, você usa o método BulkUpdate da biblioteca, que envia um único comando SQL para atualizar todos os registros. A exclusão em massa também pode ser feita de forma semelhante: var clientesInativos = context.Clientes.Where(c => c.Status == “Inativo”).ToList(); context.BulkDelete(clientesInativos); Conclusão O Entity Framework, embora altamente eficiente para muitas operações, pode não ser a escolha mais otimizada para atualizações ou exclusões em grandes volumes de dados. Para esses cenários, é altamente recomendável utilizar técnicas de bulk update e bulk delete, seja com as novas funcionalidades do EF Core 7 ou com bibliotecas especializadas como EFCore.BulkExtensions. Essas abordagens podem reduzir drasticamente o tempo de execução e o uso de recursos, garantindo que suas operações de banco de dados sejam executadas de maneira mais eficiente.
Introdução às Migrations do Entity Framework

Um dos recursos mais poderosos do Entity Framework é o sistema de migrations (migrações), que ajuda a manter o esquema do banco de dados sincronizado com o modelo de dados da aplicação ao longo do tempo. Neste artigo, vamos explorar o fluxo de desenvolvimento com migrations do Entity Framework, explicando cada etapa e os principais comandos utilizados. O que são Migrations? Migrations são uma maneira de aplicar alterações no modelo de dados (definido nas classes C#) para o banco de dados de forma incremental. Elas permitem adicionar, remover ou modificar tabelas e colunas, bem como realizar outras alterações estruturais no banco de dados. Fluxo de desenvolvimento com migrations Para entendermos como as migrations funcionam, vamos realizar a criação de um projeto .NET onde vamos implementar desde a criação dos modelos até a geração de scripts SQL. O fluxo típico de desenvolvimento com migrations inclui: Preparando o projeto Adicionar uma Migration Atualizar o Banco de Dados Reverter ou Remover uma Migration Gerar Scripts SQL Preparando o projeto Antes de adicionarmos as migrations, precisamos preparar nosso código. Isso inclui a criação de uma entidade, que no nosso exemplo será a entidade “Produto”: public class Produto { public int Id { get; set; } public string Nome { get; set; } public decimal Preco { get; set; } } Além disso, precisamos instalar os seguintes pacotes NuGet que serão utilizados para trabalhar com Entity Framework Core: Microsoft.EntityFrameworkCore.Tools: Contém as ferramentas necessárias para gerar e gerenciar as migrations. Microsoft.EntityFrameworkCore.SqlServer: Inclui o provedor do SQL Server para o Entity Framework Core. Em seguida, é necessário configurar a conexão com o banco de dados e o “DbContext”. Isso pode ser feito no arquivo “Program.cs” ou “Startup.cs”, dependendo da estrutura do seu projeto. A configuração básica do “DbContext” pode ser feita em uma classe que herda o “DbContext”. Com essas etapas concluídas, estamos prontos para criar e aplicar as migrations no banco de dados. Adicionar uma migration Após definir ou modificar seu modelo de dados, precisamos criar uma migration para registrar essas mudanças: //CLI do .NET: dotnet ef migrations add CriarTabelaProduto //Package Manager Console Add-Migration CriarTabelaProduto O comando cria automaticamente uma nova classe de migration que inclui o código necessário para aplicar as mudanças no banco de dados. Vale destacar que o nome da migration (neste caso, CriarTabelaProduto) deve ser único. Se você tentar usar um nome que já foi utilizado, o Entity Framework exibirá um erro. Portanto, escolha nomes descritivos e exclusivos para cada nova migration que você criar. Código gerado nas migrations Cada migration gerada inclui dois métodos principais: “Up” e “Down”. Esses métodos são fundamentais para a aplicação e reversão das alterações no banco de dados. O método “Up” define as operações a serem aplicadas ao banco de dados quando a migration for executada. Este método cria ou modifica o esquema do banco de dados para refletir as alterações no modelo. O método “Up” gerado pela nossa migration será semelhante a esse: protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: “produtos”, columns: table => new { Id = table.Column(type: “int”, nullable: false) .Annotation(“SqlServer:Identity”, “1, 1”), Nome = table.Column(type: “nvarchar(max)”, nullable: false), Preco = table.Column(type: “decimal(18,2)”, nullable: false) }, constraints: table => { table.PrimaryKey(“PK_produtos”, x => x.Id); }); } O método “Down” define como reverter as alterações feitas pelo método “Up”. Este método é usado para desfazer as alterações se você precisar reverter a migration. protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: “produtos”); } Atualizar o banco de dados Depois de adicionar a migration, aplique as mudanças ao banco de dados com o comando “Update-Database”: //CLI do .NET: dotnet ef database update //Package Manager Console: Update-Database Este comando executa o método “Up” da migration, aplicando as alterações ao banco de dados. Reverter ou remover uma migration Se você precisar reverter uma migration ou remover uma migration não aplicada, pode usar os seguintes comandos: //CLI do .NET: dotnet ef migrations remove //Package Manager Console: Remove-Migration Agora imagine o seguinte cenário: você começou a configurar seu projeto e criou a primeira migration para gerar a estrutura inicial do banco de dados e a tabela de “Produtos” com fizemos anteriormente. Em seguida, você criou uma segunda migration para adicionar uma tabela chamada “Vendas”. Durante a criaçãovocê criou um erro ao configurar as informações dessa tabela, ou talvez tenha percebido que os dados não estavam corretos. Nesse caso, como a migration “CriarTabelaVendas” já foi aplicada ao banco de dados, você precisa revertê-la antes de corrigir ou remover a migration. Para isso, você pode utilizar o comando a seguir para restaurar o banco de dados ao estado anterior à aplicação da migration CriarTabelaVendas: //CLI do .NET: dotnet ef database update CriarTabelaProduto //Package Manager Console: Update-Database CriarTabelaProdut Esse comando fará o downgrade do banco de dados para o estado em que estava após a primeira migration, chamada “CriarTabelaProduto”. Isso removerá a tabela “Vendas” que foi criada pela migration problemática. Gerar scripts SQL Uma funcionalidade importante do Entity Framework Migrations é a capacidade de gerar scripts SQL a partir das migrations. Isso é útil em cenários onde você precisa revisar as mudanças que serão aplicadas ao banco de dados ou deseja executar os comandos SQL manualmente, sem utilizar a aplicação para aplicar as alterações diretamente. //CLI do .NET: dotnet ef migrations script //Package Manager Console: Script-Migration Após executar esses comandos, um script SQL será gerado automaticamente. Este script contém todos os comandos necessários para aplicar as migrations e pode ser revisado ou executado manualmente no banco de dados. Se você deseja gerar um script a partir de uma migration específica, você pode fazer da seguinte forma: //CLI do .NET: dotnet ef migrations script NomeSuaMigration //Package Manager Console: Script-Migration NomeSuaMigration Outra possibilidade é a de salvar o script que foi gerado em um arquivo “.sql”. Para isso, basta executar o comando: //CLI do .NET: dotnet ef migrations script -o caminho/do/arquivo.sql //Package Manager Console: Script-Migration -Output caminho/do/arquivo.sql Usar scripts SQL em produção De acordo com as diretrizes da Microsoft, a maneira recomendada para aplicar migrations em ambientes de produção é gerar scripts
Requisições HTTP com Flurl

As requisições HTTP são parte fundamental de muitas aplicações, especialmente quando precisamos interagir com APIs externas. O Flurl é uma biblioteca que facilita tanto a construção quanto o envio de requisições HTTP. O que é o Flurl? O nome “Flurl” é um trocadilho com “Fluent URL”, refletindo a principal característica da biblioteca: oferecer recursos fluentes para construir URLs. A própria biblioteca se define como “um construtor de URLs moderno, fluente, assíncrono, fácil de testar, portátil, repleto de buzzwords (palavras da moda), e uma biblioteca que também funciona como um cliente HTTP para .NET.”. Esta descrição destaca a versatilidade e a ampla gama de recursos que o Flurl oferece para desenvolvedores que trabalham com HTTP em aplicações .NET. Preparando o ambiente Para começar a usar o Flurl, você precisa instalar o pacote NuGet no seu projeto .NET. Isso pode ser feito de duas formas: executando o comando abaixo no terminal do .NET CLI: dotnet add package Flurl.Http Ou, alternativamente, usando o NuGet Package Manager Console no Visual Studio com o seguinte comando: Install-Package Flurl.Http Para os exemplos práticos, utilizaremos o serviço “jsonplaceholder” para realizar nossas requisições HTTP e manipular dados. Este serviço fornece uma API REST falsa que é útil para testar e prototipar chamadas HTTP. Fazendo uma requisição GET Vamos começar com um exemplo básico de como fazer uma requisição HTTP GET usando o Flurl. E para este exemplo utilizaremos a seguinte classe de modelo/DTO: public class Post { public int Id { get; set; } public string Title { get; set; } public string Body { get; set; } } Para fazer uma requisição GET a fim de obter uma coleção de objetos do tipo Post, podemos proceder da seguinte forma: string apiUrl = “https://jsonplaceholder.typicode.com/posts”; var response = await apiUrl.GetJsonAsync(); Neste exemplo, a variável “apiUrl” armazena a URL da API. O método “GetJsonAsync<Post[]>()” realiza uma requisição HTTP GET para a URL especificada e automaticamente desserializa a resposta JSON em um array de objetos “Post”. Isso elimina a necessidade de código adicional para manipular a resposta JSON. Enviando dados com uma requisição POST Para enviar dados a uma API, usamos uma requisição POST. Essa requisição é ideal quando precisamos criar novos recursos no servidor. Vamos demonstrar como enviar um novo post para o serviço de API jsonplaceholder. Primeiro, definimos o objeto que queremos enviar, contendo as informações do novo post: var newPost = new { title = “Requisições HTTP com o Flurl em .NET”, body = “Flurl é uma biblioteca incrível para fazer requisições HTTP em .NET!”, userId = 1 } Neste exemplo, estamos criando um objeto anônimo ”newPost” que contém o título, o corpo da mensagem e o Id do usuário autor do post. Em seguida, enviamos esse objeto para a API usando o método POST. string apiUrl = “https://jsonplaceholder.typicode.com/posts”; var response = await apiUrl.PostJsonAsync(newPost).ReceiveJson(); O método PostJsonAsync envia uma requisição POST com o objeto “newPost” que foi automaticamente serializado para o formato JSON. Após o envio da requisição, utilizamos o método “ReceiveJson<Post>()” para aguardar a resposta e desserializá-la diretamente em um objeto “Post”. Tratamento de erros Ao realizar requisições HTTP, é importante lidar com possíveis erros que possam ocorrer durante a comunicação com o servidor, como tempo de espera excedido (timeout), falhas de rede ou respostas com códigos de status indicando erro (como 404 ou 500). O Flurl facilita o tratamento desses erros ao lançar exceções específicas que podemos capturar e tratar de forma adequada. try { string apiUrl = “https://jsonplaceholder.typicode.com/posts”; var response = await apiUrl.GetJsonAsync(); } catch (FlurlHttpTimeoutException) { Console.WriteLine(“A solicitação expirou.”); } catch (FlurlHttpException ex) when (ex.Call.Response.StatusCode == 404) { Console.WriteLine(“Recurso não encontrado.”); } catch (FlurlHttpException ex) { Console.WriteLine($”Ocorreu um erro: {ex.Message}”); } Captura de exceção FlurlHttpTimeoutException: a primeira captura de exceção específica é para “FlurlHttpTimeoutException”. Essa exceção é lançada quando a requisição HTTP excede o tempo limite definido (timeout). No caso de um timeout, a mensagem “A solicitação expirou.” é exibida no console. Isso é útil para informar ao usuário que a requisição demorou muito para receber uma resposta do servidor. Captura de exceção FlurlHttpException com condição de status code 404: A segunda captura de exceção é para “FlurlHttpException” com uma condição específica: “ex.Call.Response.StatusCode == 404”. Isso significa que, se o servidor retornar um código de status HTTP 404 (indicando que o recurso não foi encontrado), a exceção será capturada e a mensagem “Recurso não encontrado.” será exibida no console. Esta abordagem permite tratar de maneira diferenciada os casos onde o recurso solicitado não existe. Captura geral de exceção FlurlHttpException: O último catch captura qualquer outra exceção do tipo “FlurlHttpException” que não foi tratada pelas condições anteriores. Essas exceções abrangem uma ampla gama de possíveis falhas de requisição HTTP, incluindo erros de servidor (como códigos de status 500), falhas de rede, entre outros. Ao capturar essa exceção, exibimos uma mensagem genérica de erro, juntamente com a mensagem de erro específica (ex.Message), que fornece mais detalhes sobre o problema ocorrido. Download de arquivos O Flurl também simplifica o download de arquivos. Vamos ver como baixar um arquivo de uma URL: string url = “https://nextwave.education/wp-content/uploads/2024/08/CapaHttpClient.png”; string nomeArquivo = “imagem.png”; string localSalvar = @”C:UsersPublicPictures”; await url.DownloadFileAsync(LocalSalvar, NomeArquivo); Console.WriteLine($”Arquivo baixado com sucesso: {NomeArquivo}”); Utiliza-se o método “DownloadFileAsync” do Flurl para baixar o arquivo da URL fornecida e armazená-lo no diretório especificado com o nome desejado. Este método simplifica o processo de download ao cuidar da manipulação dos dados e da escrita do arquivo em disco. Acelere a sua carreira conosco! Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui Conclusão O Flurl é uma excelente opção para desenvolvedores .NET que procuram uma maneira fluente, simples e poderosa de realizar requisições HTTP. Com suporte para operações síncronas e assíncronas, tratamento de erros aprimorado, autenticação básica e OAuth 2.0, download de arquivos e integração
Null Coalescing Operators: Lidando com valores nulos em C#

Em C#, o tratamento de valores nulos é uma prática essencial para evitar erros no código, especialmente as temidas “NullReferenceException”. Para ajudar os desenvolvedores a escreverem um código mais limpo e menos propenso a erros, o C# introduziu os operadores de coalescência nula: “??” e “??=”.
Query Splitting: Melhorando a performance de consultas com Entity Framework

Neste artigo vamos explorar como o recurso Query Splitting pode ajudar a otimizar consultas que envolvem múltiplas tabelas relacionadas.
Raw SQL Queries no Entity Framework

No Entity Framework, normalmente usamos o LINQ para consultas ao banco de dados. No entanto, em situações que exigem maior controle ou quando precisamos utilizar funcionalidades específicas do banco de dados, podemos recorrer às “Raw SQL Queries” (consultas SQL brutas). Essas consultas permitem escrever SQL puro, utilizando sintaxes específicas, otimizando a performance de consultas complexas e aproveitando funcionalidades que não são facilmente replicáveis pelos métodos convencionais do Entity Framework. Executando consultas com tipos mapeados Uma das principais aplicações de consultas SQL brutas é executar comandos SELECT diretamente, mapear os resultados para entidades ou tipos personalizados e integrá-los ao contexto do EF. Para executar uma consulta SQL que retorna resultados e mapear esses resultados para uma entidade do EF: var products = dbContext.Products .FromSqlRaw(“SELECT * FROM Products WHERE Price > {0}”, 100) .ToList(); Aqui, “FromSqlRaw” permite que você insira uma consulta SQL diretamente. O {0} é um marcador de posição que será substituído pelo valor passado como argumento. Ao usar consultas SQL brutas para mapear resultados para entidades do Entity Framework, é fundamental garantir que a estrutura do resultado da consulta seja equivalente à estrutura da entidade. Isso significa que as colunas retornadas pela consulta devem corresponder aos nomes e tipos das propriedades da entidade. Caso contrário, você pode encontrar erros ou resultados inesperados ao mapear os dados retornados para a entidade. Executando comandos Além de consultas SELECT, você pode usar SQL bruto para executar comandos que não retornam resultados, como INSERT, UPDATE ou DELETE: dbContext.Database .ExecuteSqlRaw(“DELETE FROM Usuarios WHERE Id = {0}”, “3”); O método “ExecuteSqlRaw” é usado para executar comandos SQL que alteram dados no banco de dados sem retornar um conjunto de resultados. Parâmetros em consultas SQL Para evitar problemas de segurança, como injeção de SQL, é crucial usar parâmetros nomeados ao construir consultas SQL. O Entity Framework fornece suporte para passar parâmetros de maneira segura. var categoryIdParam = new SqlParameter(“@CategoryId”, 1); var products = dbContext.Products .FromSqlRaw(“SELECT * FROM Products WHERE CategoryId = @CategoryId”, categoryIdParam) .ToList(); Aqui, o “SqlParameter” é usado para passar um parâmetro para a consulta, garantindo que o valor seja corretamente escapado e prevenindo injeções SQL. Passando múltiplos parâmetros Se precisar passar mais de um parâmetro, você pode criar múltiplos objetos “SqlParameter” e passá-los como argumentos adicionais. Por exemplo: var categoryIdParam = new SqlParameter(“@CategoryId”, 1); var minPriceParam = new SqlParameter(“@MinPrice”, 50); var products = dbContext.Products .FromSqlRaw(“SELECT * FROM Products WHERE CategoryId = @CategoryId AND Price > @MinPrice”, categoryIdParam, minPriceParam) .ToList(); Neste exemplo, dois parâmetros, @CategoryId e @MinPrice, são passados para a consulta. Cada parâmetro é criado como um objeto “SqlParameter” separado e, em seguida, ambos são incluídos na chamada para “FromSqlRaw”. Isso assegura que os valores sejam devidamente repassados, prevenindo possíveis injeções SQL e mantendo a segurança da aplicação. Retornando tipos não mapeados Outra funcionalidade poderosa do Entity Framework 8 é a capacidade de retornar tipos não mapeados diretamente a partir de consultas SQL brutas. Se você deseja retornar um tipo específico, como um DTO (ProductSummary), ao invés de entidades completas, utilize o método “SqlQueryRaw” no contexto do banco de dados. var productSummaries = await dbContext.Database .SqlQueryRaw( SELECT Id, Name, Price FROM Products WHERE Price > @MinPrice, new SqlParameter(“@MinPrice”, 50) ) .ToListAsync(); Aqui, “ProductSummary” é um tipo não mapeado que representa um conjunto específico de colunas da tabela “Products”. Ao usar “SqlQueryRaw”, você retorna apenas os dados necessários e otimiza a performance e evita o carregamento da entidade completa. Stored Procedures Consultas SQL brutas são também úteis para executar stored procedures. Para detalhes sobre como executar stored procedures, consulte o artigo que em que abordamos esse tema clicando aqui. Acelere a sua carreira conosco! Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui Conclusão Raw SQL Queries no Entity Framework oferecem uma flexibilidade significativa para lidar com consultas complexas e operações específicas do banco de dados que não são facilmente expressas através da API LINQ. Ao utilizar consultas SQL brutas, os desenvolvedores podem obter um controle mais granular e otimizado sobre as interações com o banco de dados.
Realizando requisições HTTP em C# com HttpClient

O HttpClient é uma classe fundamental para realizar requisições HTTP em aplicações .NET. Ela permite que você envie e receba dados da web, seja para acessar uma API, baixar arquivos ou se comunicar com um servidor
Lendo e escrevendo arquivos de texto em C#

A manipulação de arquivos de texto é uma tarefa comum em muitas aplicações. Seja para ler dados de configuração, registrar logs ou processar informações, saber como ler e escrever arquivos de texto é fundamental. O C# fornece várias ferramentas para essas tarefas. Lendo arquivos de texto Vamos começar aprendendo como ler arquivos de texto em C#. Existem diferentes abordagens para isso, dependendo do tamanho do arquivo e das suas necessidades. Usando File.ReadAllText Se você precisa ler o conteúdo completo de um arquivo de texto e o arquivo não é muito grande, o método “File.ReadAllText” é uma opção simples e direta. Ele lê todo o conteúdo do arquivo e o retorna como uma única string: string caminhoArquivo= “caminho/para/seu/arquivo.txt”; string conteudo = File.ReadAllText(caminhoArquivo); No código acima a variável “caminhoArquivo” recebe o caminho do arquivo de texto. Após isso, o método “File.ReadAllText” busca o arquivo nesse caminho, lê o conteúdo e armazena o texto na variável “conteudo”. Usando File.ReadLines Para arquivos grandes ou quando você deseja processar o arquivo linha por linha, o “File.ReadLines” é uma opção eficiente. Ele permite que você leia cada linha separadamente: string caminhoArquivo= “caminho/para/seu/arquivo.txt”; foreach (string linha in File.ReadLines(caminhoArquivo)) { Console.WriteLine(linha ); } Nesse exemplo, “File.ReadLines(caminhoArquivo)” retorna um “IEnumerable<string>” que permite iterar sobre cada linha do arquivo. A cada iteração do loop foreach, uma nova linha é lida e impressa no console com “Console.WriteLine(linha)”. Isso garante que apenas uma linha do arquivo esteja na memória de cada vez, tornando o processamento mais eficiente para arquivos grandes. Escrevendo arquivos de texto Agora que vimos como ler arquivos, vamos explorar como escrever em arquivos de texto. Dependendo do seu objetivo, você pode optar por substituir o conteúdo existente ou adicionar novo conteúdo ao final do arquivo. Usando File.WriteAllText O método “File.WriteAllText” é usado para escrever uma string em um arquivo, substituindo qualquer conteúdo existente. Se o arquivo não existir, ele será criado. string caminhoArquivo= “caminho/para/seu/arquivo.txt”; string conteudo= “Este é o novo conteúdo do arquivo.”; File.WriteAllText(caminhoArquivo, conteudo); O “File.WriteAllText” recebe dois parâmetros: o caminho onde o arquivo está localizado ou onde ele deve ser criado e o conteúdo que deve ser gravado nesse arquivo. Usando File.AppendAllText Se você deseja adicionar texto ao final de um arquivo existente, use “File.AppendAllText”. Este método é útil para registrar logs ou adicionar informações a um arquivo sem sobrescrever o conteúdo existente. string caminhoArquivo= “caminho/para/seu/arquivo.txt”; string conteudo= “Este é o conteúdo que vai ser adicionado ao arquivo”; File.AppendAllText(caminhoArquivo, conteudo+ Environment.NewLine); Acelere a sua carreira conosco! Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui Conclusão Neste artigo exploramos as técnicas básicas para leitura e escrita de arquivos de texto em C#. A compreensão desses conceitos e a aplicação das melhores práticas ajudarão você a trabalhar com arquivos de texto de maneira mais eficaz em suas aplicações C#.