Trabalhando com tipos genéricos em C#

Os tipos genéricos são um dos pilares da programação em .NET. Eles permitem criar código reutilizável, flexível e seguro, resolvendo um dos grandes desafios do desenvolvimento de software: a criação de componentes que podem trabalhar com diferentes tipos de dados. Neste artigo, vamos explorar o que são tipos genéricos, como e por que utilizá-los, e como implementá-los em C#. O que são tipos genéricos? Tipos genéricos permitem que você defina classes, métodos, interfaces e estruturas sem especificar o tipo exato de dado com o qual eles vão trabalhar. Em vez disso, você usa parâmetros de tipo, que serão substituídos por tipos reais no momento da utilização. Por exemplo, em vez de criar uma classe “ListaDeInteiros” para armazenar inteiros e outra “ListaDeStrings” para armazenar strings, você pode criar uma classe genérica “Lista<T>”, em que  “T” representa o tipo de dado. Esse conceito é extremamente poderoso, pois oferece: Reutilização de código: ao invés de duplicar código para diferentes tipos, você cria uma única versão genérica; Segurança em tempo de compilação: o compilador verifica o tipo utilizado, prevenindo erros em tempo de execução; Manutenção mais fácil: com menos código duplicado, a manutenção torna-se mais simples e menos sujeita a erros. Exemplo de classe genérica em C# Agora vamos ver um exemplo simples da utilização de genéricos. O primeiro passo é criar uma classe genérica chamada “Caixa”, na qual vamos processar um tipo que ainda não conhecemos. public class Caixa { private T _conteudo; public Caixa(T conteudo) { _conteudo = conteudo; } public void MostrarConteudo() { Console.WriteLine($”Conteúdo: {_conteudo}”); } } Acima, criamos uma classe que vai receber um tipo quando ela for instanciada. O T entre os sinais de menor e maior (<T>) indica que a classe é genérica e pode trabalhar com qualquer tipo de dado. Dentro dela, temos o método construtor “Caixa(T conteudo)” que recebe o valor do tipo “T” e armazena em uma variável privada “_conteudo” e o método “MostrarConteudo()” que exibe o conteúdo no console. Agora, vamos criar uma variável do tipo “Caixa” e informar que ela será do tipo “int”. Isso permite que nossa classe processe números inteiros: Caixa caixaInt = new Caixa(123); Aqui, estamos dizendo que a classe “Caixa” vai trabalhar com o tipo “int” e estamos passando o valor “123” para o conteúdo. Depois disso, podemos chamar o método “MostrarConteudo”, que vai exibir no console o valor “Conteúdo: 123”: caixaInt.MostrarConteudo(); Agora, suponha que eu queira utilizar a mesma classe “Caixa”, mas desta vez passando um tipo diferente, como uma “string”. Podemos criar outra instância de “Caixa” para processar texto: Caixa caixaString = new Caixa(“Texto”); Aqui, a classe “Caixa” vai trabalhar com “string”, e o valor “Texto” será armazenado. Podemos chamar o método “MostrarConteudo” novamente, e ele vai exibir o texto: Interfaces genéricas Interfaces genéricas nos permitem criar contratos que funcionam de forma flexível com diferentes tipos de dados. Isso é útil, especialmente em cenários nos quais o comportamento é o mesmo para diferentes tipos, mas os dados podem variar. Um exemplo comum de utilização de interfaces genéricas é na criação de repositórios para operações CRUD (Create, Read, Update, Delete), que é utilizado para manipular entidades em uma aplicação. Vamos começar criando uma interface genérica que define as operações básicas de um repositório, como adicionar, obter e remover itens: public interface IRepositorio { void Adicionar(T item); T Obter(int id); void Remover(int id); } Aqui, o “T” representa o tipo de entidade com o qual o repositório vai trabalhar. Isso significa que podemos criar implementações dessa interface para qualquer tipo de entidade que nossa aplicação precise gerenciar. Os métodos “Adicionar”, “Obter” e “Remover” são genéricos e vão funcionar com qualquer tipo de dado que seja passado quando a interface for implementada. Agora vamos criar uma classe “Produto” que funcionará como entidade a ser manipulada pelo repositório: public class Produto { public int Id { get; set; } public string Nome { get; set; } } Em seguida podemos implementar o repositório para “Produto” utilizando a interface genérica “IRepositorio<T>”: public class RepositorioDeProdutos : IRepositorio { private List _produtos = new List(); public void Adicionar(Produto produto) { _produtos.Add(produto); } public Produto Obter(int id) { return _produtos.FirstOrDefault(p => p.Id == id); } public void Remover(int id) { var produto = _produtos.FirstOrDefault(p => p.Id == id); if (produto != null) { _produtos.Remove(produto); } } } Essa implementação usa uma lista de “Produto” para armazenar os itens, simulando um banco de dados em memória. O método “Adicionar” insere um novo produto na lista, o “Obter” retorna um produto com base no id, e o “Remover” exclui o produto da lista se ele for encontrado. Uma das principais vantagens de interfaces genéricas é que podemos reutilizar a mesma lógica para diferentes tipos de entidades, sem duplicar o código. Por exemplo, podemos criar um “RepositorioDeClientes” da mesma forma que criamos o “RepositorioDeProdutos”, simplesmente mudando o tipo genérico de “Produto” para “Cliente”. Restrições de tipos (constraints) em genéricos Em C#, os tipos genéricos oferecem muita flexibilidade ao permitir que classes, métodos e interfaces possam trabalhar com diferentes tipos de dados. No entanto, em alguns cenários, pode ser necessário impor restrições sobre quais tipos podem ser utilizados com genéricos. Essas restrições são chamadas de constraints. Elas garantem que o tipo passado ao genérico atenda a certos critérios, como herdar de uma classe específica, implementar uma interface, ou possuir um construtor sem parâmetros. Por exemplo, você pode querer garantir que o tipo genérico seja uma classe, e não uma struct (um tipo de valor). Isso pode ser útil quando você está lidando com referências de objetos e precisa evitar que o genérico seja aplicado a tipos de valor. public class IRepositorio where T : class { //Implementações } Quando adicionamos o “where T : class” estamos colocando uma restrição que garante que o tipo “T” seja uma classe, não permitindo o uso de outros tipos. A mesma estrutura pode ser usada para aplicar outras restrições. Por exemplo, no caso do repositório, podemos querer que apenas classes que herdam de um tipo base “Entidade” sejam usadas como parâmetro: public

Convenções de nomenclatura em C#

As convenções de nomenclatura são essenciais para garantir que o código seja legível, consistente e de fácil manutenção. Em C# e .NET, seguir as convenções de nomenclatura recomendadas melhora a clareza, facilita a colaboração entre desenvolvedores e evita ambiguidades no código. Este artigo explora as principais convenções de nomenclatura para classes, métodos, propriedades, variáveis, interfaces e enumerações, oferecendo exemplos práticos e discutindo a importância de boas práticas. A importância de seguir convenções Seguir padrões de nomenclatura é uma prática essencial em qualquer linguagem de programação, mas no contexto de C# e .NET, essas convenções são mais que recomendadas — elas são quase um padrão da comunidade. As principais razões para seguir essas convenções incluem: Legibilidade: código bem nomeado é mais fácil de ler e entender, mesmo por pessoas que não participaram do desenvolvimento inicial; Manutenção: manter e atualizar o código se torna mais simples, pois o nome das variáveis e funções já indicam sua finalidade; Consistência: seguir um padrão consistente em todo o projeto melhora a qualidade do código e facilita a revisão por outros desenvolvedores; Reuso e extensibilidade: código bem estruturado e nomeado corretamente tende a ser mais modular e fácil de expandir. Agora, vamos examinar as principais convenções de nomenclatura em C# e .NET. Nomenclatura de classes Começando pelas classes, em C# elas seguem a convenção de serem nomeadas sem PascalCase, em que cada palavra no nome começa com uma letra maiúscula. O mesmo se aplica às propriedades públicas. O nome de uma classe e suas propriedades deve ser claro e descritivo quanto ao seu propósito: public class Cliente { public string Nome { get; set; } public string Sobrenome { get; set; } public DateTime DataNascimento { get; set; } } Caso alguma propriedade da classe não esteja em PascalCase, um aviso será emitido, indicando que é sugerido corrigir a nomenclatura. Exemplos: Correto: Cliente, FormaPagamento, Pedido. Incorreto: cliente, forma_pagamento, pedido. Nomenclatura de métodos Os métodos também utilizam PascalCase. O nome do método deve indicar o que ele faz, muitas vezes iniciando com um verbo no infinitivo. Métodos devem ser nomeados de forma a refletir claramente sua função. public class Calculadora { public int SomarValores(int primeiroValor, int segundoValor) { return primeiroValor + segundoValor; } } Exemplos: Correto: CalcularDesconto, AdicionarItem, InativarCliente. Incorreto: somaValores, somar_Valores, SOMARVALORES. Nomenclatura de variáveis locais e parâmetros Para variáveis locais e parâmetros de métodos, a convenção é usar camelCase. Ou seja, a primeira letra será minúscula e, em seguida, a primeira letra de cada palavra será maiúscula. O nome deve ser descritivo. public int CalcularDesconto(int valorTotal, int desconto) { int valorFinal= valorTotal – desconto; return valorFinal; } Exemplos: Correto: AdicionarItem(ItemPedido item) ConsultarClientes(string nome, string cpf) string primeiroNome DateTime ultimaDataCompra; Incorreto: CalcularDesconto(int valor_total, int desconto) CalcularDesconto(int VALORTOTAL, int DESCONTO) int valor_final int VALORFINAL Nomenclatura de interfaces As interfaces em C# também usam PascalCase, e adicionalmente podem conter uma letra “I” no início do nome para indicar que se trata de uma interface. O nome da interface deve descrever uma ação ou comportamento. public interface ICommand { void Executar(); } Nomenclatura de enums As enumerações também seguem a convenção de PascalCase, em que o nome da enum e cada um de seus valores começa com letra maiúscula. A Microsoft recomenda que os valores sejam claros e descritivos, representando estados ou categorias de forma precisa, com substantivos ou adjetivos. Na comunidade .NET, é comum o uso do prefixo “E” antes do nome da enumeração (como EStatus), para identificar visualmente que se trata de um tipo enum. Essa prática ajuda especialmente em projetos com muitas entidades e classes. public enum Status { Pendente, Enviado, Entregue, Cancelado } Exemplos: Correto: public enum TipoPagamento public enum EStatusPagamento Incorreto: public enum tipoPagamento public enum TIPO_PAGAMENTO Boas práticas em nomenclatura Evite abreviações e nomes ambíguos Evitar abreviações e nomes que não explicam o propósito é fundamental. Ainda que nomes mais curtos possam parecer convenientes, é melhor priorizar a clareza. Nomes explícitos facilitam a compreensão do código, mesmo que sejam um pouco mais longos. Correto: BuscarUsuarioPorId, ProcessarCodigoBarras Incorreto: Buscar, BusUsuPId Nomes claros ajudam a identificar rapidamente a função ou objetivo do método ou variável, tornando o código mais intuitivo e acessível para desenvolvedores que trabalham no mesmo projeto. Evite o uso de identificadores que conflitam com palavras-chave É recomendável evitar o uso de identificadores que entrem em conflito com palavras-chave de linguagens de programação amplamente usadas. De acordo com a Regra 4 da CLS (Common Language Specification), todas as linguagens compatíveis devem fornecer um mecanismo que permita o acesso a itens nomeados que usam uma palavra-chave dessa linguagem como identificador. O C#, por exemplo, utiliza o sinal “@” como um mecanismo de escape nesse caso. string @string; ou public class Example { public void @class() { // Código } } Embora seja possível utilizar “@” para evitar conflitos com palavras-chave, é uma boa prática evitar ao máximo o uso de palavras reservadas como identificadores. Métodos ou variáveis com escape “@” podem ser confusos e complicados de manter, além de dificultar a legibilidade do código. 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 Seguir as convenções de nomenclatura em C# e .NET é uma prática essencial para a criação de código limpo, legível e fácil de manter. O uso correto de PascalCase e camelCase, evitar abreviações, garantir que nomes sejam claros e descritivos, e evitar conflitos com palavras-chave são práticas que devem ser seguidas rigorosamente. Manter essas boas práticas resultará em um código mais profissional e, o mais importante, mais fácil de colaborar e expandir em longo prazo.

Adicionando JavaScript à sua página HTML

Incorporar JavaScript em uma página HTML é uma prática essencial para desenvolvedores web que desejam adicionar interatividade e dinamismo ao conteúdo. Nos artigos anteriores, discutimos os fundamentos do HTML e como adicionar CSS a uma Página HTML, que formam a base da estrutura e do estilo de uma página web. Agora, ao integrar o JavaScript, completamos o trio de tecnologias que possibilitam a construção de experiências interativas e dinâmicas para os nossos usuários. Para que serve JavaScript? O JavaScript é uma linguagem de programação, inicialmente voltada para o desenvolvimento de funcionalidades dinâmicas e interativas em páginas web. Enquanto o HTML define a estrutura e o CSS estiliza o conteúdo, o JavaScript adiciona comportamento ao site, permitindo a criação de animações, validação de formulários, interações complexas com o usuário e até mesmo o controle de elementos na página sem a necessidade de recarregamento. Isso torna a experiência do usuário mais rica e responsiva, uma vez que é possível responder rapidamente às ações do visitante, como cliques, teclas pressionadas e outras interações. Como incorporar JavaScript no HTML Conforme veremos a seguir, existem algumas abordagens possíveis para adicionar código JavaScript em uma página HTML. Cada uma possui características específicas que a torna mais ou menos adequada para cada cenário. JavaScript inline em atributos HTML A forma mais direta de adicionar JavaScript é incorporá-lo diretamente nos atributos de elementos HTML, como o  “onclick”, e outros eventos. Esse método é útil para pequenas funções ou eventos específicos, mas tem limitações quando se trata de manutenção e organização do código. Por exemplo: Clique-me Neste exemplo, o código JavaScript alert(‘Botão clicado!’) está diretamente associado ao evento “onclick” do botão. Sempre que o botão for clicado, uma mensagem de alerta aparecerá na tela. Vantagens e desvantagens do JavaScript inline Vantagens: simples de implementar para ações específicas e pequenas; Desvantagens: torna o HTML desorganizado e difícil de manter em projetos maiores. Mistura lógica de programação com marcação, o que pode dificultar a manutenção. JavaScript em uma tag script local no HTML Usar a tag “<script>” dentro do próprio arquivo HTML permite escrever código JavaScript mais estruturado e organizado, sem misturá-lo diretamente com a marcação HTML. Esse método é útil para scripts curtos e funciona bem para páginas em que o JavaScript não será reutilizado. JavaScript em Tag Script Clique-me Aqui, a função “exibirMensagem()” está definida dentro de uma tag “<script>” que está dentro do <head>. Quando o botão é clicado, o evento “onclick” chama essa função, exibindo o alerta. Neste exemplo, temos uma abordagem mista, uma vez que utilizamos o evento “onclick” diretamente na tag <button>. Porém, também é possível fazer todo o trabalho dentro da tag <script>, sem “misturar” HTML com JavaScript: JavaScript em Tag Script Clique-me No exemplo acima, atribuímos um id ao botão, para facilitar a referência a ele dentro do JavaScript. E lá na tag <script>, utilizamos o método “document.getElementById” para obter essa referência. Em seguida, com o botão referenciado, chamamos o método “addEventListener”, que atribui uma função ao evento “click” do elemento. Dessa forma, o código HTML ficou mais limpo e toda lógica de programação ficou restrita ao bloco de JavaScript. Onde colocar a tag <script>: <head> ou <body> No <head>: colocar o JavaScript no <head> significa que ele será carregado antes de o conteúdo da página ser exibido. Isso pode aumentar o tempo de carregamento para o usuário, especialmente em scripts maiores, já que o navegador precisa processar o JavaScript antes de renderizar o conteúdo; No <body>: colocar a tag <script> no final do <body> permite que o conteúdo da página seja carregado primeiro, melhorando a experiência do usuário ao reduzir o tempo de carregamento inicial. Essa abordagem é recomendada para scripts que não precisam ser executados imediatamente. JavaScript em arquivo Externo Por fim, em projetos maiores ou em que o JavaScript é reutilizado, o uso de um arquivo JavaScript externo é a prática mais recomendada. Isso mantém o código organizado, facilita a manutenção e permite que o arquivo seja armazenado em cache pelo navegador, acelerando o carregamento das próximas visitas. Para exemplificar, vamos criar um arquivo chamado “script.js” na pasta do nosso projeto e adicionar uma  função. Organização dos arquivos: Código JavaScript que deve ser armazenado no arquivo “script.js”: function exibirMensagem() { alert(‘Mensagem a partir de uma tag script!’); } document.getElementById(‘btnAlerta’).addEventListener(‘click’, exibirMensagem); Agora vamos incluir o arquivo externo na página HTML usando a tag <script src=”script.js”></script>: JavaScript com Arquivo Externo Clique-me O atributo “defer”, ou em português “adiar”, é adicionado à tag <script> para indicar que o navegador deve carregar o script de forma assíncrona e executá-lo apenas após o carregamento completo do HTML, melhorando o desempenho. Vantagens e desvantagens do JavaScript externo Vantagens: Código mais organizado, fácil de manter e reutilizável em múltiplas páginas. Pode ser armazenado em cache, aumentando a velocidade de carregamento nas visitas subsequentes; Desvantagens: Requer o gerenciamento dos arquivos externos e a definição correta dos caminhos no HTML. Conclusão Escolher como e onde adicionar JavaScript ao HTML depende das necessidades do projeto e do impacto na performance. Para scripts simples e específicos, o uso inline pode ser suficiente, mas para projetos maiores, o ideal é manter o JavaScript em arquivos externos e carregar esses arquivos de forma que não atrasem o carregamento da página. Compreender essas diferenças permite que desenvolvedores escolham as melhores práticas para garantir uma experiência de usuário eficiente e fluida.

Trabalhando com flags em .NET

No desenvolvimento de software, frequentemente nos deparamos com situações em que precisamos trabalhar com múltiplas opções ou estados simultaneamente. Para essas situações, o uso de flags em pode ser muito útil. As flags permitem combinar e manipular valores de forma eficiente, usando operações de bit a bit. O que são flags? Flags são comumente usadas em enums (enumerações) para representar combinações de valores usando operações bitwise (bit a bit). Imagine que você precisa representar diferentes permissões ou estados de um objeto, e essas permissões podem ser combinadas. Flags são ideais para isso, pois elas permitem representar múltiplos estados ao mesmo tempo usando um único valor. Enum e flags: a diferença Um enum tradicional permite escolher apenas um valor de cada vez. Por exemplo, um enum que representa os dias da semana permite que uma variável armazene apenas um dia por vez: public enum DiasDaSemana { Domingo = 1, Segunda = 2, Terça = 3, Quarta = 4, Quinta = 5, Sexta = 6, Sábado = 7 } Mas, e se você quiser representar mais de um dia ao mesmo tempo, como por exemplo “Segunda e Terça”? Para isso, o C# oferece enums com flags, que permitem representar combinações de valores. Usando enum com flags Para usar enum com flags, é necessário entender que os valores definidos no enum permitem combinar múltiplas opções, representando, por exemplo, uma seleção de dias da semana. Com o atributo [Flags], podemos tratar os valores do enum como uma combinação de flags. Para exemplificar isso, vamos criar um enum com flags: [Flags] public enum DiasDaSemana { Nenhum = 0, Domingo = 1, Segunda = 2, Terça = 4, Quarta = 8, Quinta = 16, Sexta = 32, Sábado = 64 } Dessa forma podemos combinar múltiplos valores desse enum usando o operador  “|”. Por exemplo: DiasDaSemana diasSelecionados = DiasDaSemana.Segunda | DiasDaSemana.Terça; Console.WriteLine(diasSelecionados); Nesse exemplo, combinamos os dias “Segunda” e “Terça”, e o enum resultante mantém ambos os valores e imprimindo  na tela o texto “Segunda, Terça”. Por que potências de 2? Se observarmos bem, quando criamos o enum com flags acima, utilizamos potências de 2 ao invés de sequência comum dos enumeradores (1,2,3,4 …). Isso acontece porque quando utilizamos potências de 2, cada valor ocupa uma posição distinta em seu mapa de bits. Por exemplo, considere as seguintes potências de 2 representadas em binário: 1 (2^0) = 0001 2 (2^1) = 0010 4 (2^2) = 0100 8 (2^3) = 1000 Cada valor tem apenas um bit ativo e em uma posição única. Isso garante que, ao combinar esses valores utilizando a operação OR bitwise (|), os bits ativos se somem, permitindo que várias flags sejam combinadas sem causar sobreposição. Para entendermos melhor, vamos criar uma flag de permissões: [Flags] public enum Permissoes { Nenhuma = 0, // 0000 Ler = 1, // 0001 Escrever = 2, // 0010 Executar = 4, // 0100 Deletar = 8 // 1000 } Se quisermos combinar “Ler” e “Escrever”, a operação Ler | Escrever resultará em: 0001 (Ler = 1) 0010 (Escrever = 2) ——- 0011 (Ler, Escrever = 3) O valor resultante (0011 em binário, ou 3 em decimal) representa a combinação de ambas as permissões sem que haja conflito ou sobreposição de bits. Operações comuns com flags Verificando valores Para verificar se uma flag específica está definida em uma combinação, utilizamos o operador “&”: bool contemSegunda = (diasSelecionados & DiasDaSemana.Segunda) == DiasDaSemana.Segunda; Console.WriteLine(contemSegunda); Aqui, estamos verificando se “Segunda” faz parte de “diasSelecionados”. Adicionando valores Para adicionar um valor a um enum flag, usamos o operador “|”: diasSelecionados |= DiasDaSemana.Quarta; Console.WriteLine(diasSelecionados); Nesse exemplo, adicionamos “Quarta” à combinação de dias já existente Removendo valores Para remover uma flag de uma combinação, usamos o operador “&” com o complemento bitwise “~”: diasSelecionados &= ~DiasDaSemana.Terça; Console.WriteLine(diasSelecionados); Aqui, removemos “Terça” da combinação. Quando usar flags? Flags são extremamente úteis em diversas situações, como: Permissões de acesso: quando você quer representar várias permissões (ler, escrever, executar, etc.) para um arquivo ou recurso; Configurações: ao armazenar múltiplas opções de configuração em um único campo; Estados de um sistema: quando um objeto pode estar em vários estados simultâneos (por exemplo, conectado e autenticado). Boas práticas Algumas boas práticas ao utilizar as flags são: Use potências de 2: sempre defina os valores do enum como potências de 2 para garantir que as combinações funcionem corretamente; Crie um valor “Nenhum”: incluir um valor Nenhum = 0 é uma boa prática, pois representa a ausência de qualquer flag; Facilite a leitura: combine o uso de enums com métodos descritivos para tornar o código mais legível e fácil de entender. Exemplo prático: sistema de permissões Vamos agora aplicar flags para um sistema de permissões de usuários. Após criar uma aplicação de console, vamos utilizar o flags de permissões que criamos acima e criar uma entidade chamada “Usuario”: public class Usuario { public string Nome { get; set; } public Permissoes PermissoesDeUsuario { get; set; } } Em seguida, vamos implementar o nosso código no “program.cs” do nosso projeto: public class Program { public static void Main(string[] args) { //Nosso código; } } Vamos adicionar um novo ao qual onde vamos dar as permissões de “Ler” e “Escrever”: Usuario usuario = new Usuario { Nome = “João”, PermissoesDeUsuario = Permissoes.Ler | Permissoes.Escrever }; Console.WriteLine($”{usuario.Nome} tem as seguintes permissões: {usuario.PermissoesDeUsuario}”); Depois, vamos adicionar mais uma permissão para ao usuário, que é a de “Executar”: usuario.PermissoesDeUsuario |= Permissoes.Executar; Console.WriteLine($”Novas permissões: {usuario.PermissoesDeUsuario}”); E por fim, vamos verificar se ele tem permissão para poder realizar deleções: bool podeDeletar = (usuario.PermissoesDeUsuario & Permissoes.Deletar) == Permissoes.Deletar; Console.WriteLine($”Pode deletar? {podeDeletar}”); Com isso, nós conseguimos criar um sistema simples de gerenciamento de permissões de usuários. 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 Flags no

Carregando configurações do AppSettings em .NET

No desenvolvimento de aplicações .NET, a configuração é um aspecto crucial para garantir que parâmetros importantes sejam acessados de forma eficiente e flexível. O arquivo “appsettings.json” é um dos métodos mais comuns e utilizados para armazenar configurações em projetos .NET, fornecendo uma maneira fácil de manter informações sensíveis e variáveis configuráveis fora do código-fonte. Estrutura do appsettings.json Primeiro, vamos começar com um exemplo básico de um arquivo “appsettings.json”. Ele é um arquivo de configuração baseado em JSON que pode armazenar informações como strings de conexão, variáveis de ambiente, e outros parâmetros importantes. Abaixo temos um exemplo: { “Logging”: { “LogLevel”: { “Default”: “Information”, “System”: “Warning”, “Microsoft”: “Warning” } }, “ConnectionStrings”: { “DefaultConnection”: “Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;” }, “AppSettings”: { “ApplicationName”: “Minha Aplicação”, “Version”: “1.0.0”, “SupportedCultures”: [ “en-US”, “pt-BR” ] } } Nesse exemplo, temos três seções principais: Logging: Configurações de nível de log. ConnectionStrings: Strings de conexão com o banco de dados. AppSettings: Informações customizadas da aplicação. Adicionando o serviço de configuração Antes de começar a ler as configurações, é importante garantir que o serviço de configuração seja adicionado ao pipeline da sua aplicação. Para aplicações como ASP.NET Core, isso já é configurado automaticamente. Entretanto, para outros tipos de projetos, como aplicações de console, você precisa configurar manualmente o carregamento do “appsettings.json”. Em uma aplicação de console, você precisa configurar manualmente o carregamento do “appsettings.json” no arquivo “Program.cs”. Isso é feito adicionando o pacote “Microsoft.Extensions.Configuration.Json” e configurando o builder manualmente. Veja como fazer isso: Install-Package Microsoft.Extensions.Configuration.Json ou dotnet add package Microsoft.Extensions.Configuration.Json Adicione a configuração no “Program.cs”: using Microsoft.Extensions.Configuration; class Program { static void Main(string[] args) { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile(“appSettings.json”, optional: true, reloadOnChange: true); IConfiguration configuration = builder.Build(); var appName = configuration[“AppSettings:ApplicationName”]; Console.WriteLine($”Application Name: {appName}”); } } Nesse exemplo, estamos configurando manualmente o “ConfigurationBuilder” para carregar as configurações do arquivo “appSettings.json”. Em seguida, usamos a interface “IConfiguration” para acessar os valores. Neste exemplo é necessário criar o arquivo “appSettings.json” na pasta de execução da aplicação. Lendo configurações com IConfiguration A forma mais básica de acessar as configurações no .NET é utilizando a interface “IConfiguration”. Ela fornece uma maneira simples de acessar os valores diretamente, seja qual for a estrutura ou tipo do dado. Para o nosso exemplo, vamos  supor que queremos acessar a string de conexão com o banco de dados. Podemos fazer isso injetando “IConfiguration” em um controlador da nossa API. [ApiController] [Route(“api/[controller]”)] public class ConfigController : ControllerBase { private readonly IConfiguration _configuration; public ConfigController(IConfiguration configuration) { _configuration = configuration; } [HttpGet(“GetDatabaseConnection”)] public IActionResult GetDatabaseConnection() { var connectionString = _configuration.GetConnectionString(“DefaultConnection”); return Ok(connectionString); } } No código acima, utilizamos o método “GetConnectionString” para obter a string de conexão configurada no “appsettings.json”. A interface “IConfiguration” é injetada no construtor da classe e usada para acessar a configuração. Usando o método GetSection O método “GetSection” permite acessar uma parte do arquivo “appsettings.json” como uma seção distinta. Isso é útil quando você deseja trabalhar com uma subseção completa, como uma estrutura mais complexa. O valor retornado por “GetSection” ainda é um objeto de configuração, de modo que você pode acessar subseções e valores dentro dela. var applicationName = _configuration.GetSection(“AppSettings”)[“ApplicationName”]; Aqui, “GetSection(“AppSettings”)” isola a seção “AppSettings” do “appsettings.json”, e então “[“ApplicationName”]” acessa o valor dessa chave dentro da seção. Acessando diretamente via indexação de chave Uma segunda abordagem é utilizar a notação de chave com : (dois pontos), permite acessar valores de forma direta sem ter que chamar “GetSection” explicitamente. Essa notação representa a hierarquia no arquivo JSON. var applicationName = _configuration[“AppSettings:ApplicationName”]; Nesse caso, estamos utilizando a chave “AppSettings:ApplicationName” diretamente, o que evita a necessidade de chamar o “GetSection”. Quando usamos o “IConfiguration” para acessar diretamente as configurações no “appsettings.json” estamos acessando valores manualmente usando strings de chaves. Embora isso funcione bem em aplicações simples, pode se tornar problemático em sistemas maiores, pois como as chaves são passadas como strings, erros de digitação podem ocorrer facilmente. Se o nome de uma chave mudar no “appsettings.json”, seria necessário atualizar manualmente todas as referências ao longo da aplicação, o que aumenta o risco de inconsistências e erros difíceis de detectar. Mapeando configurações com IOptions Para resolver esses problemas, o “IOptions<T>” oferece uma abordagem mais robusta e segura. Ao usar “IOptions<T>”, mapeamos as configurações do “appsettings.json” para uma classe fortemente tipada. Isso reduz a chance de erros, pois passamos a usar propriedades fortemente tipadas em vez de strings, e se o nome de uma chave mudar, só precisamos ajustar o mapeamento uma vez na classe de configuração, em vez de em todo o código. Além disso, usando “IOptions<T>”, o próprio .NET valida a configuração durante o carregamento, o que evita que valores ausentes ou incorretos causem erros silenciosos de tempo de execução. Mapeamento com IOptions Primeiro, criamos uma classe que corresponde às seções do arquivo “appsettings.json”. No nosso exemplo, vamos mapear a seção “AppSettings”: public class AppSettings { public string ApplicationName { get; set; } public string Version { get; set; } public List SupportedCultures { get; set; } } Configurando o IOptions Agora, vamos configurar o “IOptions<T>” dentro do arquivo “Program.cs” para mapear as configurações para a classe “AppSettings”. var builder = WebApplication.CreateBuilder(args); builder.Services.Configure(builder.Configuration.GetSection(“AppSettings”)); O comando “builder.Configuration.GetSection(“AppSettings”)” acessa a seção “AppSettings” do arquivo “appsettings.json” e mapeia suas configurações para a classe “AppSettings” que criamos, tornando essas informações disponíveis de forma estruturada. Em seguida, o “builder.Services.Configure<AppSettings>” registra essa classe no contêiner de dependências da aplicação, permitindo que ela seja injetada em qualquer serviço ou controlador que precise acessar essas configurações. Acessando as configurações em um controller Agora que configuramos o “IOptions<T>”, podemos injetá-lo em qualquer lugar da aplicação, como em um controlador de API, para acessar as configurações de forma tipada. [ApiController] [Route(“api/[controller]”)] public class ConfigController : ControllerBase { private readonly IOptions _appSettings; public ConfigController(IOptions appSettings) { _appSettings = appSettings; } [HttpGet(“GetAppSettings”)] public IActionResult GetAppSettings() { var appSettings = _appSettings.Value; return Ok(appSettings); } } O “_appSettings.Value” permite acessar a instância da classe “AppSettings” que foi configurada por meio de “IOptions<AppSettings>”. Ao utilizar essa abordagem, as configurações armazenadas no arquivo “appsettings.json” são carregadas de forma estruturada

Adicionando CSS a uma página HTML

O CSS (Cascading Style Sheets) é uma linguagem de estilo utilizada para definir a apresentação de documentos HTML. Ao separar a estrutura (HTML) da apresentação (CSS), é possível criar páginas web mais organizadas, manuteníveis e esteticamente agradáveis. Neste artigo vamos explorar as diferentes maneiras de incluir CSS em uma página HTML, apresentar exemplos práticos e discutir boas práticas para a organização do CSS. Antes de começarmos, se você está dando os primeiros passos no desenvolvimento web, recomendamos a leitura do artigo Como criar sua primeira página HTML. Ele fornece uma base sobre HTML, a linguagem de marcação que dá estrutura às páginas web. Com esse conhecimento, você estará pronto para avançar na personalização visual com CSS, que abordaremos a seguir. Para que serve CSS? Assim como o HTML define a estrutura do conteúdo, o CSS define como os elementos HTML devem ser exibidos visualmente. Isso pode incluir a definição de cores, fontes, espaçamentos, alinhamentos e até animações. Estrutura do CSS O CSS utiliza uma estrutura simples composta por propriedades e valores. Propriedade: É o que será alterado no elemento HTML, como color (cor), font-size (tamanho da fonte), margin (margem), entre outros. Valor: Define o comportamento da propriedade, como red (vermelho) para color ou 24px para font-size.   Cada conjunto de propriedade+valor é chamado de declaração (declaration) e quando inseridos no atributo style de um elemento, ou seja, de forma inline como veremos a seguir, devem ser separados por ponto e vírgula, da seguinte forma: color: red;font-size: 24px; Porém, quando o CSS é declarado dentro da tag <style> ou em um arquivo externo, sua estrutura é composta por regras (rules), cuja estrutura é a seguinte: Cada regra é composta por um seletor e um bloco de declarações. O seletor é a especificação de quais elementos HTML serão afetados pelas declarações. Por exemplo, podemos aplicar uma regra a todas as tags <p> de uma página. Tipos de inclusão de CSS Estilos inline Estilos inline são aplicados diretamente aos elementos HTML usando o atributo “style”. Isso pode ser útil em situações pontuais, nas quais você precisa aplicar um estilo específico e rápido, sem mexer no restante do código. Título com Estilo Inline No código acima, são definidos os estilos a serem aplicados ao elemento <h1>. Cada propriedade é seguida de um valor. Prós: Simplicidade: é fácil e rápido adicionar estilos inline diretamente em um elemento. Específico: os estilos aplicam-se imediatamente ao elemento específico, sem afetar outros. Contras: Difícil de manter: em projetos maiores, fica confuso controlar e ajustar múltiplos estilos inline. Não reutilizável: cada estilo precisa ser redefinido para cada elemento, o que resulta em repetição de código. “Poluição do código”: misturar conteúdo (HTML) com estilo (CSS) pode tornar o código HTML bagunçado e difícil de ler. Estilos internos Os estilos internos são definidos dentro da tag “<style>” no “<head>” da página HTML. Esta abordagem é útil para quando você precisa aplicar estilos a uma única página, sem a necessidade de criar um arquivo CSS separado. No exemplo acima foi especificado o seletor, que define quais elementos serão afetados pelo estilo (neste caso, as tags <body> e <h1>), seguidos das regras CSS que definem as modificações a serem aplicadas a esses elementos, como a cor de fundo da página e a cor do texto dos cabeçalhos. Prós: Centralizado por página: todos os estilos são agrupados em um só lugar, dentro da própria página HTML. Sem arquivos externos: você não precisa de arquivos CSS adicionais, o que pode ser conveniente para páginas simples ou temporárias. Contras: Não reutilizável: os estilos internos só afetam aquela página específica, tornando difícil aplicar os mesmos estilos em outras páginas. Pouco eficiente: se várias páginas precisarem dos mesmos estilos, você terá que duplicar o código, o que prejudica a manutenção. Diminui a separação de responsabilidades: embora melhor que o estilo inline, ainda mistura HTML com CSS, dificultando a organização em projetos maiores. Estilos externos A forma mais organizada e recomendada de adicionar CSS a uma página web é utilizando um arquivo externo. Com essa abordagem, você pode separar os estilos em um arquivo .css independente, permitindo reutilização e fácil manutenção em várias páginas. No seu editor de código, crie um arquivo com o nome de sua preferência (ex:  “estilo.css”) dentro de uma pasta chamada estilos. Neste arquivo, adicione suas regras de estilo CSS. Por exemplo: body { background-color: #f0f0f0; } p { color: red; } Agora, abra seu arquivo HTML (ex: index.html) e adicione o link para o arquivo CSS no cabeçalho do documento, entre as tags <head> e </head>. Veja o exemplo: Salve ambos os arquivos (index.html e estilos/estilo.css) e abra o HTML no navegador. Se tudo foi feito corretamente, o texto do parágrafo deverá aparecer na cor vermelha. Vantagens do CSS externo O uso de arquivos CSS externos traz várias vantagens: Manutenção simplificada: Alterar um arquivo CSS pode atualizar o estilo de várias páginas simultaneamente, sem a necessidade de modificar cada documento HTML individualmente. Reutilização: Você pode reutilizar o mesmo arquivo CSS em diferentes páginas ou até em diferentes projetos. Separação de responsabilidades: O conteúdo (HTML) e a apresentação (CSS) ficam separados, o que promove um código mais limpo e organizado. Conclusão Existem diversas maneiras de incluir CSS em uma página HTML, cada uma com seus prós e contras. Para projetos simples, estilos inline ou internos podem funcionar, mas para projetos maiores, é essencial seguir as boas práticas e utilizar estilos externos, mantendo uma clara separação entre conteúdo e apresentação. Agora que você sabe como incluir CSS em suas páginas HTML, pode explorar as diversas possibilidades que essa linguagem de estilização oferece, alterando a aparência de suas páginas.

Como criar sua primeira página HTML

HTML (HyperText Markup Language) é a base de toda página web. Se você está começando a aprender desenvolvimento web, entender HTML é o primeiro passo essencial. Neste artigo, vamos explorar o que é HTML, como ele funciona e guiá-lo passo a passo na criação da sua primeira página HTML. Introdução ao HTML HTML é uma linguagem de marcação usada para criar e estruturar o conteúdo de páginas na web. Ele funciona organizando textos, imagens, links e outros elementos em uma página, permitindo que os navegadores entendam e exibam corretamente essas informações. Cada página na web é construída com HTML, o que torna esse conhecimento crucial para qualquer desenvolvedor web. Saber o básico de HTML permite que você crie páginas simples e compreenda o funcionamento de sites mais complexos. Conforme você avança, poderá integrar outras tecnologias, como CSS e JavaScript, mas tudo começa com o HTML. Passo a passo para criar a primeira página Agora que entendemos o que é o HTML, vamos criar a sua primeira página. Abrir um editor de texto Para começar, você pode usar qualquer editor de texto simples, como o Bloco de Notas no Windows ou um editor mais sofisticado como o Visual Studio Code (VS Code), que facilita a escrita de código com sugestões automáticas e realce de sintaxe. Para o nosso exemplo utilizaremos o Visual Studio Code. Criar o arquivo .html Selecione ou crie uma pasta no seu computador onde deseja armazenar sua página HTML. Essa pasta será o local onde seus arquivos HTML e outros recursos (como imagens e folhas de estilo) serão mantidos organizados. Após selecionar, crie um novo arquivo. Ao salvar o arquivo, dê um nome que faça sentido para o conteúdo da página. Por exemplo, se for sua primeira página, um nome como “minhaPagina.html” funciona bem. Certifique-se de salvar o arquivo com a extensão .html, pois isso indica ao sistema e ao navegador que este é um arquivo HTML. Agora é só clicar no arquivo criado e você está pronto para começar a escrever o código HTML! Inserir a estrutura básica No seu arquivo HTML, insira a seguinte estrutura básica de um documento HTML: Minha Primeira Página HTML Para entendermos a estrutura básica que envolve um documento HTML, podemos fazer uma analogia com o corpo humano, em que temos a cabeça (<head>) e o corpo (<body>), cada uma com sua função específica. Declaração <!DOCTYPE html> No topo do documento HTML, temos a declaração <!DOCTYPE html>. Ela informa ao navegador que o arquivo deve ser interpretado como HTML5, que é a versão mais recente da linguagem de marcação até o momento. Isso é fundamental para que o navegador entenda corretamente o conteúdo da página e aplique as regras adequadas ao exibir o seu site. A tag <html> Logo após a declaração <!DOCTYPE html>, encontramos a tag <html>, que envolve todo o conteúdo da página web. Tudo dentro desta tag faz parte do documento HTML. A tag <html> também pode incluir o atributo “lang” para definir o idioma da página. Por exemplo, lang=”pt-br” define que a página está em português do Brasil. Cabeça (<head>) – O cérebro do documento Dentro da tag <html>, temos a tag <head>, que funciona como o cérebro de uma página web. Assim como a cabeça de uma pessoa armazena informações importantes (como o cérebro, que processa tudo), a tag <head> armazena dados essenciais para o funcionamento da página, mas que não são diretamente visíveis para o usuário. Dentro dela podemos colocar informações como: Meta informações: a tag <meta charset=”UTF-8″> define a codificação de caracteres, garantindo que letras acentuadas e símbolos sejam exibidos corretamente. Caso esta tag não seja utilizada, o navegador não sabe qual codificação de caracteres deve usar. Como resultado, ele pode interpretar os acentos de maneira errada, substituindo-os por símbolos estranhos. Por exemplo, em um parágrafo como:  <p>Essa é uma página com caracteres acentuados: á, é, í, ó, ú, ç.</p> A exibição no navegador pode ficar assim: “Essa é uma página com caracteres acentuados: á, é, í, ó, ú, ç.” Título da página: a tag <title> define o nome da página que aparece na aba do navegador, semelhante ao exemplo abaixo. Links externos: como arquivos de CSS (para definir o estilo da página) ou scripts de JavaScript (para adicionar interatividade). Mesmo que o conteúdo do <head> não seja visível no navegador, ele é crucial para a interpretação correta do documento, assim como o cérebro é vital para o funcionamento do corpo. Corpo (<body>) – O que é visível A tag <body>, por outro lado, é o que define o corpo da página web, ou seja, tudo o que o usuário realmente vê e interage. Assim como no corpo humano, onde os braços, pernas e rosto são visíveis, tudo dentro do <body> aparece na tela do navegador. No <body>, podemos incluir: Títulos: o <h1> é usado para o título principal da página, que geralmente tem maior destaque. Ex: <h1>Bem-vindo à minha página!</h1> Parágrafos: o <p> é utilizado para blocos de texto descritivos ou explicativos. Ex: <p>Essa é a minha primeira página HTML.</p> Imagens: o <img> exibe uma imagem no navegador. Ele tem um atributo chamado  “src” que define a localização da imagem, e um outro atributo chamado “alt” que descreve a imagem caso ela não seja carregada. Caso a você tenha o link de uma imagem, você pode adicionar da seguinte forma: Ex:<img src=”C:/arquivos/imagem.png” alt=”Imagem de exemplo” /> Links: o <a> cria um hiperlink que vai direcionar o usuário para uma outra página ou para um link interno. Ele tem o atributo “href” que define o endereço do link, e o “target=”_blank”” que vai informar ao navegador que o  link deve ser aberto em uma nova aba. Ex: <p>Se quiser aprender mais sobre programação, visite o site da <a href=”https://nextwave.education/” target=”_blank”>Next Wave Education</a> para mais informações.</p> Essa separação clara entre a cabeça (<head>) e o corpo (<body>) permite que a página seja organizada de forma lógica. A cabeça contém as instruções e configurações para o navegador, enquanto o corpo exibe os elementos visíveis e interativos para

Introdução aos testes de unidade com XUnit no .NET

Os testes de unidade são uma técnica fundamental para garantir que o comportamento de pequenas partes de um sistema funcione conforme esperado. No .NET, o XUnit é um framework popular para escrever testes de unidade, devido à sua simplicidade e flexibilidade. Boas práticas na criação de testes de unidade Antes de começarmos com a implementação do nosso exemplo, é importante destacar algumas boas práticas para escrever testes de unidade eficazes: Testes independentes: cada teste deve ser independente dos outros. Isso significa que a ordem de execução dos testes não deve afetar seus resultados. Nomeação clara: O nome do teste deve descrever claramente o que ele está testando. Isso facilita o entendimento de qual comportamento está sendo validado. Arrange-Act-Assert (AAA): a estrutura padrão para testes de unidade, que facilita a leitura e compreensão do código de teste: Arrange: configura o cenário inicial. Act: executa a ação a ser testada. Assert: verifica se o resultado é o esperado. Manter o teste simples e específico: testes devem ser simples, focando em apenas uma funcionalidade ou cenário por vez. Isso facilita a identificação do que falhou em caso de erro. Evite dependências externas: testes de unidade não devem depender de serviços externos, como bancos de dados ou APIs. Utilize mocking ou stubs para simular dependências. Teste cenários positivos e negativos: teste tanto os caminhos em que tudo ocorre como esperado (cenários positivos) quanto os erros esperados e exceções (cenários negativos). Implementando um sistema de estoque Antes de começarmos a escrever nossos testes, precisamos de um código que será testado. Para isso, vamos desenvolver um sistema de gerenciamento de estoque. O primeiro passo é criar um projeto de console que conterá uma entidade chamada “Produto”. public class Produto { public string Nome { get; set; } public decimal Preco { get; set; } public int Quantidade { get; set; } public decimal ValorTotal { get; set; } public Produto(string nome, decimal preco, int quantidade) { Nome = nome; Preco = preco; Quantidade = quantidade; ValorTotal = quantidade * preco; } } Após isso, vamos criar uma classe chamada “Estoque” em que vamos implementar o gerenciamento: private readonly List _produtos = new List(); // Adicionar produto ao estoque public void AdicionarProduto(Produto produto) { var produtoExistente = _produtos.FirstOrDefault(p => p.Nome == produto.Nome); if (produtoExistente != null) { produtoExistente.Quantidade += produto.Quantidade; } else { _produtos.Add(produto); } } // Remover produto do estoque public void RemoverProduto(string nome, int quantidade) { var produto = _produtos.FirstOrDefault(p => p.Nome == nome); if (produto == null || produto.Quantidade < quantidade) { throw new InvalidOperationException("Produto indisponível ou quantidade insuficiente."); } produto.Quantidade -= quantidade; if (produto.Quantidade == 0) { _produtos.Remove(produto); } } // Retorna a lista de produtos public List ListarProdutos() { return _produtos; } Implementando testes com XUnit Agora que a lógica do sistema foi implementada, vamos garantir que tudo funcione corretamente através de testes de unidade. O primeiro passo é criar um projeto de teste do tipo XUnit dentro da solução onde está o projeto principal. O resultado da estrutura da solução deve ser algo semelhante a isso: Com o projeto de testes criado, clique com o botão direito do mouse sobre o projeto “GestaoEstoqueTest” e a seguir clique em “Adicionar” e selecione a opção “Referência de Projeto…”.  Na janela que abriu, marque o projeto “GestaoEstoque” e clique em OK: Agora, vamos criar um teste para verificar se a adição de produtos ao estoque está funcionando corretamente, seguindo a abordagem padrão de Arrange, Act e Assert: [Fact] public void AdicionarProduto_DeveAdicionarProdutoAoEstoque() { // Arrange var estoque = new Estoque(); var produto = new Produto("Laptop", 5000m, 10); decimal valorTotalEsperado = 50000m; // Preço esperado após a adição decimal valorTotalDiferenteDoEsperado = 20500m; // Um preço incorreto // Act estoque.AdicionarProduto(produto); // Assert var resposta = estoque.ListarProdutos(); Assert.Single(resposta); Assert.Equal(valorTotalEsperado, resposta[0].ValorTotal); Assert.NotEqual(valorTotalDiferenteDoEsperado, resposta[0].ValorTotal); Assert.NotNull(resposta); Assert.NotEmpty(resposta); Assert.InRange(resposta[0].Preco, 1000m, 15000m); } Arrange: Nesta fase, preparamos tudo o que é necessário para o teste. Aqui criamos um novo estoque (Estoque), e definimos um produto (Produto) com o nome “Laptop”, preço de 5000m, e quantidade de 10 unidades. Além disso, definimos dois valores esperados para o teste:   valorTotalEsperado: 50000m, que representa o valor total correto do produto após a adição. valorTotalDiferenteDoEsperado: 20500m, um valor total incorreto que usamos para garantir que o sistema não atribua preços errados.   Act: No Act, chamamos a ação que queremos testar. Nesse caso, é o método AdicionarProduto(produto), que adiciona o produto “Laptop” ao estoque. Este é o comportamento central que estamos verificando neste teste: a adição correta de produtos ao estoque.   Single A primeira verificação é se a lista de produtos, após a adição, contém exatamente um item. Isso é importante porque garante que o método “AdicionarProduto” inseriu apenas um produto no estoque e que não houve duplicação ou falha. Se houver mais ou menos de um item na lista, o teste falha.   Equal Aqui, validamos que o preço do produto adicionado ao estoque corresponde ao valor esperado. Isso confirma que o preço foi corretamente calculado e armazenado no produto. Se o valor armazenado no estoque não corresponder ao esperado, o teste falhará, indicando um possível problema na lógica de adição de produtos.   NotEqual Este assert serve para garantir que o preço do produto não é igual a um valor incorreto, como o que foi definido em “valorTotalDiferenteDoEsperado”. Ele atua como uma camada extra de segurança, confirmando que o sistema não atribuiu valores errados ao produto, prevenindo erros que poderiam passar despercebidos.   NotNull Este assert assegura que a lista de produtos retornada pelo método “ListarProdutos” não é nula, ou seja, que o método retornou uma lista válida. Esse assert serve para garantir que o sistema esteja manipulando os dados corretamente e que a coleção de produtos foi inicializada corretamente.   NotEmpty Além de verificar que a lista não é nula, também garantimos que ela não está vazia. Isso confirma que o produto foi de fato adicionado ao estoque, proporcionando maior confiança de que o processo de adição foi bem-sucedido e que o estoque está funcionando conforme o esperado.   InRange Por

Reconhecimento facial em .NET com Kairos

A validação biométrica está se tornando uma ferramenta indispensável em um mundo cada vez mais digital, desempenhando um papel crucial em áreas como segurança e autenticação de usuários. Com o crescimento das preocupações relacionadas à segurança e à privacidade, soluções que utilizam características únicas do ser humano, como impressões digitais, reconhecimento facial e íris, têm se destacado pela eficácia e precisão. O que é a API Kairos? A Kairos é uma API de reconhecimento facial que utiliza inteligência artificial para identificar e verificar pessoas com base em características faciais. A API oferece a funcionalidade de verificação biométrica, que compara duas imagens para confirmar a identidade do usuário. Implementação da validação biométrica Para ilustrar o nosso exemplo, vamos considerar um cenário em que queremos validar a identidade de um funcionário usando uma selfie e uma imagem que foi armazenada no momento do seu cadastro. O primeiro passo que devemos tomar é definir as variáveis que serão utilizadas ao longo do código: string apiUrl = “https://idv-eu.kairos.com/v0.1/biometric-verification”; string appId = “IdentificadorAPP”; string appKey = “ChaveDaSuaAPI”; string selfieFilePath = @”C:CaminhodaImagemSelfie.jpg”; string imageFilePath = @”C:CaminhodaImagemArmazenada.png”; A variável “apiUrl” contém a URL da API Kairos para verificação biométrica.  As variáveis “appId” e “appKey” são as credenciais necessárias para autenticar a requisição na API, e é importante que sejam substituídas pelos valores correspondentes de sua conta na Kairos. “selfieFilePath” e “imageFilePath” armazenam os caminhos para os arquivos de imagem que serão utilizados no processo de validação. É crucial garantir que esses caminhos estejam corretos e que as imagens existam para evitar erros durante a execução. if (!File.Exists(selfieFilePath) || !File.Exists(imageFilePath)) { Console.WriteLine(“Erro: Um ou mais arquivos de imagem não foram encontrados.”); return; } Aqui, realizamos uma verificação para garantir que os arquivos de imagem especificados existam. Se qualquer um dos arquivos não for encontrado, vamos exibir uma mensagem de erro, e o programa será encerrado. Essa etapa é importante para evitar que a aplicação falhe mais adiante ao tentar acessar arquivos inexistentes. using (var client = new HttpClient()) { var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri(apiUrl), Headers = { { “app_id”, appId }, { “app_key”, appKey }, }, Content = new MultipartFormDataContent { // Adiciona a selfie do usuário new StreamContent(File.OpenRead(selfieFilePath)) { Headers = { ContentDisposition = new ContentDispositionHeaderValue(“form-data”) { Name = “selfie”, FileName = Path.GetFileName(selfieFilePath), } } }, // Adiciona a imagem armazenada new StreamContent(File.OpenRead(imageFilePath)) { Headers = { ContentDisposition = new ContentDispositionHeaderValue(“form-data”) { Name = “image”, FileName = Path.GetFileName(imageFilePath), } } }, }, }; Neste bloco criamos uma instância do “HttpClient”, que será utilizada para enviar a requisição.  Em seguida, configuramos um “HttpRequestMessage”, definindo o método HTTP como POST e especificando a URL da API.  Os cabeçalhos de autenticação são adicionados utilizando “app_id” e “app_key”.  O conteúdo da requisição é um “MultipartFormDataContent”, que permite enviar múltiplos arquivos como parte da requisição. Aqui, utilizamos “StreamContent” para ler os arquivos de imagem diretamente do sistema de arquivos. Os cabeçalhos “ContentDisposition” são configurados para cada arquivo, especificando o nome do campo e o nome do arquivo, o que é necessário para a API entender quais dados estão sendo enviados. using (var response = await client.SendAsync(request)) { response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body); } Por fim, vamos enviar a requisição utilizando o método “SendAsync” do “HttpClient”. Após enviar a requisição, usamos “EnsureSuccessStatusCode” para garantir que a resposta da API foi bem-sucedida. Caso contrário, uma exceção será lançada. Em seguida, lemos o corpo da resposta e o exibimos no console.  A resposta da API será algo semelhante a isso. Vamos entender os principais campos deste json: { “api_req_uid”: “2f960394-96c9-4403-b34d-92b1e33f7b7c”, “processed_at”: “2024-10-02 02:50:30”, “requested_at”: “2024-10-02 02:50:30”, “response_code”: 0, “response_data”: { “biometric”: { “image”: { “faces_match_confidences”: [ 1.0 ], “faces_predicted”: [ { “confidence”: 0.9357, “delta”: { “x”: 199.7969, “y”: 233.3345 }, “top_left”: { “x”: 233.5239, “y”: 162.5957 } } ], “img”: { “height”: 480, “width”: 640 } }, “max_facematch_confidence”: 1.0, “selfie”: { “faces_liveness”: 1.0, “faces_predicted”: [ { “confidence”: 1.0, “delta”: { “x”: 161.1886, “y”: 219.9249 }, “top_left”: { “x”: 297.8376, “y”: 196.7119 } } ], “img”: { “height”: 799, “width”: 735 } } }, “decision”: { “details”: [], “reject_score”: 0.0, “review_score”: 0.0, “warning_score”: 0.0 } } } response_code: Indica o status da resposta. Um código 0 geralmente significa que a solicitação foi processada com sucesso, sem erros. faces_match_confidences: Um array que armazena as pontuações de confiança da correspondência entre os rostos detectados na imagem submetida e na selfie. Neste exemplo, a confiança máxima foi de 1.0, indicando uma correspondência precisa. faces_liveness: Indica a verificação de vivacidade da selfie, ou seja, se a imagem foi tirada de uma pessoa viva. Um valor de 1.0 significa que a vivacidade foi confirmada com sucesso. reject_score: Indica a pontuação de rejeição da verificação. Um valor 0.0 significa que a verificação foi bem-sucedida. review_score e warning_score: Pontuações que indicam se há necessidade de revisão manual ou algum aviso relacionado ao processo de verificação. No exemplo, ambas estão em 0.0, sugerindo que não há problemas ou necessidade de revisão. Em casos onde a verificação não é bem-sucedida, o sistema pode gerar uma decisão negativa, como no exemplo a seguir: “decision”:{ “details”:[ { “code”:”FAILED_FACE_MATCH”, “confidence”:1.0, “decision”:”reject”, “description”:”Faces do not match; threshold failed: 0.2285; x < 0.4" } ], "reject_score":1.0, "review_score":0.0, "warning_score":0.0 } Neste exemplo, o sistema retorna um “reject_score” de 1.0, indicando que a verificação foi rejeitada. A descrição informa que “Os rostos não correspondem; limite falhou: 0.2285; x < 0.4”, o que significa que a pontuação de semelhança foi inferior ao limite necessário (0.4). Isso resultou em uma decisão negativa, pois semelhanças abaixo de 0.4 são consideradas insuficientes para validar a correspondência entre os rostos. 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 A validação biométrica está se

Utilizando Include e ThenInclude no Entity Framework

Quando estamos lidando com consultas que envolvem relações entre entidades, o EF oferece métodos eficientes para incluir dados relacionados, como “Include”, “ThenInclude”. Este artigo irá explorar esses métodos, explicando suas funcionalidades e apresentando exemplos práticos de uso. Lazy Loading No Entity Framework (EF), o Lazy Loading, ou carregamento tardio, é um comportamento padrão em que as entidades relacionadas não são carregadas automaticamente junto com a entidade principal. Esse comportamento é vantajoso, pois nem sempre precisamos de todos os dados relacionados à entidade, evitando assim o carregamento de informações desnecessárias, melhorando o desempenho em algumas situações ao reduzir a quantidade de dados inicialmente buscados. Eager Loading No entanto, há casos em que é necessário carregar as entidades relacionadas junto com a entidade principal. Isso é especialmente importante quando sabemos antecipadamente que precisaremos dessas informações. Nesses cenários, o Eager Loading, ou carregamento antecipado se torna uma abordagem mais apropriada. Usando os métodos “Include” e “ThenInclude”, o EF permite carregarmos as entidades relacionadas de uma só vez, em uma única consulta, garantindo que todas as informações necessárias estejam disponíveis imediatamente. Consulta sem Include Para ilustrar o funcionamento dessas técnicas no Entity Framework, vamos tomar como exemplo um cenário em que temos a entidade “Pedido”, a qual contém um “Cliente” que, por sua vez, contém um “Endereco”. Ou seja, temos dois níveis de relacionamento entre as entidades: Ao realizarmos uma consulta no banco de dados, apenas os dados da entidade principal, “Pedido”, são carregados. Os dados relacionados, como o “Cliente” e seu “Endereco”, não são carregados imediatamente. Por exemplo: var pedidos = _context.Pedidos.ToList(); No resultado da consulta acima teríamos apenas os dados da entidade principal, “Pedido”, enquanto a propriedade de navegação “Cliente” viria nula, pois ela não foi explicitamente requisitada. Nesse caso os dados retornados seriam os seguintes: [ { “pedidoId”: 1, “descricao”: “Pedido 001”, “clienteId”: 101, “cliente”: null }, { “pedidoId”: 2, “descricao”: “Pedido 002”, “clienteId”: 102, “cliente”: null } ] Utilizando Include Conforme foi comentado anteriormente, o Lazy Loading pode ser bastante útil para evitar o carregamento antecipado de dados que não serão necessários no primeiro momento. Porém, desta vez desejamos carregar também os dados do cliente vinculado ao pedido, para isso utilizaremos o método “Include”, da seguinte forma: var pedidos = _context.Pedidos .Include(p => p.Cliente) .ToList(); Neste exemplo estamos passando como parâmetro para o método “Include” uma expressão lambda que define qual propriedade de navegação desejamos carregar. Ao fazer isso, o Entity Framework irá resolver o relacionamento que foi configurado e realizar a consulta adequada para trazer os dados dependentes. Dessa vez os dados retornados seriam os seguintes: [ { “pedidoId”: 1, “descricao”: “Pedido 001”, “clienteId”: 101, “cliente”: { “clienteId”: 101, “nome”: “João Silva”, “endereco”: null } }, { “pedidoId”: 2, “descricao”: “Pedido 002”, “clienteId”: 102, “cliente”: { “clienteId”: 102, “nome”: “Maria Souza”, “endereco”: null } } ] Utilizando ThenInclude No exemplo anterior, carregamos uma propriedade de navegação a partir da entidade principal da consulta. Porém, note que a classe “Cliente” possui uma propriedade “Endereco” que veio nula na consulta acima. Isso ocorre porque como temos mais um relacionamento, é preciso carregá-lo explicitamente, assim como foi feito com o primeiro. Porém, dessa vez, como desejamos incluir uma entidade que está relacionada a uma outra que já foi incluída, precisamos utilizar o método “ThenInclude”, realizando um tipo de carregamento “em cascata”: var pedidos = _context.Pedidos .Include(p => p.Cliente) .ThenInclude(c => c.Endereco) .ToList(); Agora, os resultados retornados incluem também os dados da entidade “Endereco”, relacionada ao cliente de cada pedido: [ { “pedidoId”: 1, “descricao”: “Pedido 001”, “clienteId”: 101, “cliente”: { “clienteId”: 101, “nome”: “João Silva”, “endereco”: { “enderecoId”: 501, “rua”: “Rua das Flores”, “cidade”: “São Paulo” } } }, { “pedidoId”: 2, “descricao”: “Pedido 002”, “clienteId”: 102, “cliente”: { “clienteId”: 102, “nome”: “Maria Souza”, “endereco”: { “enderecoId”: 502, “rua”: “Avenida Paulista”, “cidade”: “São Paulo” } } } ] Utilizando múltiplos Include e ThenInclude Caso você precise carregar múltiplas entidade relacionadas, também é possível. Por exemplo, o código abaixo inclui as propriedades “Pagamento” e “Itens” do pedido: var pedidos = _context.Pedidos .Include(p => p.Cliente) .ThenInclude(c => c.Endereco) .Include(p => p.Pagamento) .Include(p => p.Itens) .ToList(); Da mesma forma, se cada uma dessas propriedades de navegação possuir entidades relacionadas, basta utilizar o “ThenInclude” novamente: var pedidos = _context.Pedidos .Include(p => p.Cliente) .ThenInclude(c => c.Endereco) .Include(p => p.Pagamento) .ThenInclude(p => p.Parcelas) .Include(p => p.Itens) .ThenInclude(p => p.Produto) .ToList(); Para incluir vários subníveis de relacionamento, basta usar o “ThenInclude” em cascata. Por exemplo, supondo que a classe “Endereco” possua uma propriedade “Pais”, ela pode ser incluída na consulta da seguinte forma: var pedidos = _context.Pedidos .Include(p => p.Cliente) .ThenInclude(c => c.Endereco) .ThenInclude(e => e.Pais) .ToList(); Já para o caso de uma mesma propriedade de navegação possuir mais de uma entidade relacionada, é necessário usar a sequência “Include+ThenInclude” repetidas vezes. Por exemplo, se o cliente possui uma propriedade “Contatos”, representando seus e-mails e telefones, ela pode ser incluída na consulta assim: var pedidos = _context.Pedidos .Include(p => p.Cliente) .ThenInclude(c => c.Endereco) .Include(p => p.Cliente) .ThenInclude(c => c.Contatos) .ToList(); 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 Conhecer o funcionamento dos métodos “Include” e “ThenInclude” é fundamental para a manipulação eficaz de dados relacionados no Entity Framework. Partindo dos exemplos ilustrados neste artigo você pode aplicar essa técnica em seus projetos e construir consultas completas.