TREINAMENTOS Desenvolvimento Web com ASP.NET MVC 4
Desenvolvimento Web com ASP.NET MVC 4 25 de junho de 2012 Sumário i Sobre a K19 1 Seguro Treinamento 2 Termo de Uso 3 Cursos 4 1 Banco de Dados 1 1.1 Sistemas Gerenciadores de Banco de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 SQL Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Bases de Dados (Databases) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.4 Tabelas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.5 CRUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.6 Chaves Primária e Estrangeira . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.7 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2 ADO.NET 23 2.1 Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2 ODBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3 ODBC Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.4 Criando uma conexão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.5 Inserindo registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.7 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.8 SQL Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.10 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.11 Listando registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.12 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.13 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.14 Connection Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 www.k19.com.br i
S UMÁRIO ii 2.15 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.16 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.17 Desafios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3 Entity Framework 37 3.1 Múltiplas sintaxes da linguagem SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.2 Orientação a Objetos VS Modelo Relacional . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.3 Ferramentas ORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.4 Configuração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.5 Mapeamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.6 Manipulando entidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.7 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.8 Repositórios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4 Visão Geral do ASP.NET MVC 53 4.1 Necessidades de uma aplicação web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.2 ASP .NET MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.3 MVC e Front Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.4 Visual Web Developer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.5 Exemplo de uma Aplicação Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.7 Integração com Entity Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.8 Scaffold . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5 Camada de Apresentação 67 5.1 Razor e ASPX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 5.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.3 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.4 ViewBag e Strogly Typed Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.5 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.6 HTML Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 5.7 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.8 Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.10 Partial views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 5.11 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 6 Camada de Controle 99 6.1 Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 6.2 ActionResult . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 6.3 Parâmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 6.5 TempData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 6.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 7 Rotas 107 7.1 Adicionando uma rota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 7.2 Adicionando Parâmetros nas Rotas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 ii www.k19.com.br
iii S UMÁRIO 7.3 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 8 Validação 111 8.1 Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 8.2 View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 8.3 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 8.4 Anotações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 8.5 Validação no lado do Cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 8.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 9 Sessão 119 9.1 Identificando os navegadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 9.2 Sessões no ASP .NET MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 9.3 Session Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 9.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 10 Autenticação 125 10.1 Filtro de Autenticação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 10.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 11 Tratamento de Erros 131 11.1 Try-Catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 11.2 Custom Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 11.3 Http Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 11.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 A ASP.NET Web API 139 A.1 REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 A.2 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 A.3 URIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 A.4 Operações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 A.5 Media Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 A.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 B Migrations 147 B.1 Passo a Passo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 B.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 C Projeto 161 C.1 Modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 C.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 C.3 Persistência - Mapeamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 C.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 C.5 Persistência - Configuração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.7 Persistência - Repositórios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.8 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 C.10 Apresentação - Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 C.11 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 C.12 Cadastrando e Listando Seleções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 www.k19.com.br iii
S UMÁRIO iv C.13 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 C.14 Removendo Seleções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 C.15 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 C.16 Cadastrando, Listando e Removendo Jogadores . . . . . . . . . . . . . . . . . . . . . . . 174 C.17 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 C.18 Removendo Jogadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 C.19 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 C.20 Membership e Autorização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 C.21 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 C.22 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 C.23 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 C.24 Controle de Erro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 C.25 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 C.26 Enviando email . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 C.27 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 D Respostas 193 iv www.k19.com.br
1 S UMÁRIO Sobre a K19 A K19 é uma empresa especializada na capacitação de desenvolvedores de software. Sua equipe é composta por profissionais formados em Ciência da Computação pela Universidade de São Paulo (USP) e que possuem vasta experiência em treinamento de profissionais para área de TI. O principal objetivo da K19 é oferecer treinamentos de máxima qualidade e relacionados às prin- cipais tecnologias utilizadas pelas empresas. Através desses treinamentos, seus alunos se tornam capacitados para atuar no mercado de trabalho. Visando a máxima qualidade, a K19 mantém as suas apostilas em constante renovação e melho- ria, oferece instalações físicas apropriadas para o ensino e seus instrutores estão sempre atualizados didática e tecnicamente. www.k19.com.br 1
S UMÁRIO 2 Seguro Treinamento Na K19 o aluno faz o curso quantas vezes quiser! Comprometida com o aprendizado e com a satisfação dos seus alunos, a K19 é a única que pos- sui o Seguro Treinamento. Ao contratar um curso, o aluno poderá refazê-lo quantas vezes desejar mediante a disponibilidade de vagas e pagamento da franquia do Seguro Treinamento. As vagas não preenchidas até um dia antes do início de uma turma da K19 serão destinadas ao alunos que desejam utilizar o Seguro Treinamento. O valor da franquia para utilizar o Seguro Treina- mento é 10% do valor total do curso. 2 www.k19.com.br
3 S UMÁRIO Termo de Uso Termo de Uso Todo o conteúdo desta apostila é propriedade da K19 Treinamentos. A apostila pode ser utilizada livremente para estudo pessoal . Além disso, este material didático pode ser utilizado como material de apoio em cursos de ensino superior desde que a instituição correspondente seja reconhecida pelo MEC (Ministério da Educação) e que a K19 seja citada explicitamente como proprietária do material. É proibida qualquer utilização desse material que não se enquadre nas condições acima sem o prévio consentimento formal, por escrito, da K19 Treinamentos. O uso indevido está sujeito às medidas legais cabíveis. www.k19.com.br 3
S UMÁRIO 4 TR EIN AM EN TR TO EIN S TREINAMENTOS AM EN TO S Conheça os nossos cursos K01- Lógica de Programação K11 - Orientação a Objetos em Java K12 - Desenvolvimento Web com JSF2 e JPA2 K21 - Persistência com JPA2 e Hibernate K22 - Desenvolvimento Web Avançado com JFS2, EJB3.1 e CDI K23 - Integração de Sistemas com Webservices, JMS e EJB K31 - C# e Orientação a Objetos K32 - Desenvolvimento Web com ASP.NET MVC www.k19.com.br/cursos 4 www.k19.com.br
CAPÍTULO B ANCO DE D ADOS 1 Em geral, as aplicações necessitam armazenar dados de forma persistente para consultá-los pos- teriormente. Por exemplo, a aplicação de uma livraria precisa armazenar os dados dos livros e dos autores de forma persistente. Suponha que esses dados sejam armazenados em arquivos do sistema operacional. Vários fato- res importantes nos levam a descartar tal opção. A seguir, apresentamos as principais dificuldades a serem consideradas na persistência de dados. Segurança: O acesso às informações potencialmente confidenciais deve ser controlado de forma que apenas usuários e sistemas autorizados possam manipulá-las. Integridade: Restrições relacionadas aos dados armazenados devem ser respeitadas para que as in- formações estejam sempre consistentes. Consulta: O tempo gasto para realizar as consultas aos dados armazenados deve ser o menor possí- vel. Concorrência: Em geral, diversos sistemas e usuários acessarão concorrentemente as informações armazenadas. Apesar disso, a integridade dos dados deve ser preservada. Considerando todos esses aspectos, concluímos que um sistema complexo seria necessário para persistir as informações de uma aplicação de maneira adequada. Felizmente, tal tipo de sistema já existe e é conhecido como Sistema Gerenciador de Banco de Dados (SGBD). Figura 1.1: Sistema Gerenciador de Banco de Dados Sistemas Gerenciadores de Banco de Dados No mercado, há diversas opções de sistemas gerenciadores de banco de dados. Os mais populares são: • Oracle Database • SQL Server www.k19.com.br 1
B ANCO DE D ADOS 2 • MySQL Server • PostgreSQL SQL Server Neste treinamento, utilizaremos o SQL Server Express, que é mantido pela Microsoft. O SQL Server Express pode ser obtido a partir do site: http://www.microsoft.com/express/Database/. Microsoft SQL Server Management Studio Express Para interagir com o SQL Server Express, utilizaremos um cliente com interface gráfica chamado de Microsoft SQL Server Management Studio Express. Bases de Dados (Databases) Um sistema gerenciador de banco de dados é capaz de gerenciar informações de diversos sistemas ao mesmo tempo. Por exemplo, as informações dos clientes de um banco, além dos produtos de uma loja virtual ou dos livros de uma livraria. Suponha que os dados fossem mantidos sem nenhuma separação lógica. Implementar regras de segurança específicas seria extremamente complexo. Tais regras criam restrições quanto ao con- teúdo que pode ser acessado por cada usuário. Por exemplo, determinado usuário poderia ter per- missão de acesso aos dados dos clientes do banco, mas não às informações dos produtos da loja virtual, ou dos livros da livraria. Para obter uma organização melhor, os dados são armazenados separadamente em um SGDB. Daí surge o conceito de base de dados (database). Uma base de dados é um agrupamento lógico das informações de um determinado domínio. Criando uma base de dados no SQL Server Express Para criar uma base de dados no SQL Server Express, utilizamos o comando CREATE DATABASE. 2 www.k19.com.br
3 B ANCO DE D ADOS www.k19.com.br 3
B ANCO DE D ADOS 4 Repare que além da base de dados livraria há outras bases. Essas bases foram criadas automati- camente pelo próprio SQL Server Express para teste ou para guardar algumas configurações. Quando uma base de dados não é mais necessária, ela pode ser removida através do comando DROP DATABASE. Tabelas Um servidor de banco de dados é dividido em bases de dados com o intuito de separar as informa- ções de domínios diferentes. Nessa mesma linha de raciocínio, podemos dividir os dados de uma base a fim de agrupá-los segundo as suas correlações. Essa separação é feita através de tabelas. Por exemplo, no sistema de um banco, é interessante separar o saldo e o limite de uma conta, do nome e CPF de um cliente. Então, poderíamos criar uma tabela para os dados relacionados às contas e outra para os dados relacionados aos clientes. Cliente Conta nome idade cpf numero saldo limite José 27 31875638735 1 1000 500 Maria 32 30045667856 2 2000 700 Tabela 1.1: Tabelas para armazenar os dados relacionados aos clientes e às contas Uma tabela é formada por registros (linhas) e os registros são formados por campos (colunas). Por exemplo, considere uma tabela para armazenar as informações dos clientes de um banco. Cada registro dessa tabela armazena em seus campos os dados de um determinado cliente. Criando tabelas no SQL Server Express As tabelas no SQL Server Express são criadas através do comando CREATE TABLE. Na criação de uma tabela é necessário definir quais são os nomes e os tipos das colunas. 4 www.k19.com.br
5 B ANCO DE D ADOS www.k19.com.br 5
B ANCO DE D ADOS 6 No SQL Server os nomes das tabelas são precedidas pelo ID do usuário que possui a tabela. No caso do usuário sa, o ID é dbo. Portanto o nome da tabela Livros fica dbo.Livros. Se uma tabela não for mais desejada ela pode ser removida através do comando DROP TABLE. CRUD As operações básicas para manipular os dados persistidos são: inserir, ler, alterar e remover. Essas operações são realizadas através de uma linguagem de consulta denominada SQL (Structu- red Query Language). Essa linguagem oferece quatro comandos básicos: INSERT, SELECT, UPDATE 6 www.k19.com.br
7 B ANCO DE D ADOS e DELETE. Esses comandos são utilizados para inserir, ler, alterar e remover registros, respectiva- mente. www.k19.com.br 7
B ANCO DE D ADOS 8 8 www.k19.com.br
9 B ANCO DE D ADOS Chaves Primária e Estrangeira Suponha que os livros da nossa livraria sejam classificados por editoras. As editoras possuem nome e telefone. Para armazenar esses dados, uma nova tabela deveria ser criada. Nesse momento, teríamos duas tabelas (Livro e Editora). Constantemente, a aplicação da livraria deverá descobrir qual é a editora de um determinado livro ou quais são os livros de uma determinada editora. Para isso, os registros da tabela Editora devem estar relacionados aos da tabela Livro. Na tabela Livro, poderíamos adicionar uma coluna para armazenar o nome da editora dos livros. Dessa forma, se alguém quiser recuperar as informações da editora de um determinado livro, deve www.k19.com.br 9
B ANCO DE D ADOS 10 consultar a tabela Livro para obter o nome da editora correspondente. Depois, com esse nome, deve consultar a tabela Editora para obter as informações da editora. Porém, há um problema nessa abordagem. A tabela Editora aceita duas editoras com o mesmo nome. Dessa forma, eventualmente, não conseguiríamos descobrir os dados corretos da editora de um determinado livro. Para resolver esse problema, deveríamos criar uma restrição na tabela Editora que proíba a inserção de editoras com o mesmo nome. Para resolver esse problema no SQL Server Express, poderíamos adicionar a propriedade UNI- QUE no campo nome da tabela Editora. Porém, ainda teríamos mais um problema. Na tabela Li- vro, poderíamos adicionar registros vinculados a editoras inexistentes, pois não há nenhuma relação explícita entre as tabelas. Para solucionar esses problemas, devemos utilizar o conceito de chave primária e chave estrangeira. Toda tabela pode ter uma chave primária, que é um conjunto de um ou mais campos que de- vem ser únicos para cada registro. Normalmente, um campo numérico é escolhido para ser a chave primária de uma tabela, pois as consultas podem ser realizadas com melhor desempenho. Então, poderíamos adicionar um campo numérico na tabela Editora e torná-lo chave primária. Vamos chamar esse campo de id. Na tabela Livro, podemos adicionar um campo numérico chamado editora_id que deve ser utilizado para guardar o valor da chave primária da editora correspondente ao livro. Além disso, o campo editora_id deve estar explicitamente vinculado com o campo id da tabela Editora. Para estabelecer esse vínculo, o campo editora_id da tabela Livro deve ser uma chave estrangeira associada à chave primária da tabela Editora. Uma chave estrangeira é um conjunto de uma ou mais colunas de uma tabela que possuem va- lores iguais aos da chave primária de outra tabela. Com a definição da chave estrangeira, um livro não pode ser inserido com o valor do campo editora_id inválido. Caso tentássemos fazer isso, obteríamos uma mensagem de erro. Exercícios de Fixação 1 Abra o Microsoft SQL Server Management Studio Express utilizando NOME_DA_MAQUINA SQLEXPRESS como Server Name, SQL Server Authentication como Authentication, sa como Login e sa como Password. 10 www.k19.com.br
11 B ANCO DE D ADOS 2 Caso exista uma base de dados chamada Livraria, remova-a conforme a figura abaixo: 3 Crie uma nova base de dados chamada livraria, conforme mostrado na figura abaixo. Você vai utilizar esta base nos exercícios seguintes. www.k19.com.br 11
B ANCO DE D ADOS 12 4 Crie uma tabela chamada Editoras conforme as figuras abaixo. 12 www.k19.com.br
13 B ANCO DE D ADOS Altere os campos para torná-los obrigatórios, NÃO permitindo que eles fiquem em branco NU LL. Além disso o campo Id deve ser uma chave primária. www.k19.com.br 13
B ANCO DE D ADOS 14 O campo Id dever ser incrementado automaticamente. Defina ele com a propriedade Identity segundo a figura abaixo: 14 www.k19.com.br
15 B ANCO DE D ADOS 5 Crie uma tabela chamada Livros conforme as figuras abaixo: Lembrando de NÃO marcar a opção ALLOW NULL. Além disso o campo Id deve ser uma chave primária e automaticamente incrementada. Você precisa tornar o campo EditoraId uma chave estrangeira. Clique com o botão direito sobre a coluna EditoraId e selecione a opção Relantioships..., conforme a figura abaixo: www.k19.com.br 15
B ANCO DE D ADOS 16 Devemos acrescentar o relacionamento entre livro e editora. Clique em Add e posteriormente no botão à direita na linha Tables and Columns Specification. Devemos informar qual é a chave primária que a coluna EditoraId da tabela Livros faz referência. 16 www.k19.com.br
17 B ANCO DE D ADOS Para isto, informe a tabela Editoras como Primary Key Table e indique a coluna Id como a chave primária referenciada. Selecione a coluna EditoraId como a coluna que irá fazer referência a chave primária da tabela Editoras. 6 Adicione alguns registros na tabela Editoras. Veja exemplos na figura abaixo: Adicione alguns registros na tabela Livros. Veja exemplos na figura abaixo: www.k19.com.br 17
B ANCO DE D ADOS 18 7 Consulte os registros da tabela Editoras, e em seguida consulte a tabela Livros. Veja exemplos logo abaixo: 18 www.k19.com.br
19 B ANCO DE D ADOS 8 Altere alguns dos registros da tabela Livros. Veja o exemplo abaixo: 9 Altere alguns dos registros da tabela Editoras. Veja o exemplo abaixo: www.k19.com.br 19
B ANCO DE D ADOS 20 10 Remova alguns registros da tabela Livros. Veja o exemplo abaixo: 11 Remova alguns registros da tabela Editoras. Preste atenção para não remover uma editora que tenha algum livro relacionado já adicionado no banco. Veja o exemplo abaixo: 20 www.k19.com.br
21 B ANCO DE D ADOS 12 Faça uma consulta para buscar todos os livros de uma determinada editora. Veja um exemplo na figura abaixo: www.k19.com.br 21
B ANCO DE D ADOS 22 22 www.k19.com.br
CAPÍTULO ADO.NET 2 No capítulo anterior, aprendemos que utilizar bancos de dados é uma ótima alternativa para armazenar os dados de uma aplicação. Entretanto, você deve ter percebido que as interfaces dispo- níveis para interagir com o SQL Server Express não podem ser utilizadas por qualquer pessoa. Para utilizá-las, é necessário conhecer a linguagem SQL e os conceitos do modelo relacional. Em geral, as interfaces dos outros SGDBs exigem os mesmos conhecimentos. SELECT * FROM tbl_funcionarios WHERE nome LIKE ‘%jonas%’; INSERT INTO tbl_funcionarios (nome, codigo, salario) VALUES (’Rafael’, 1234, 1000); Figura 2.1: Usuários comuns não possuem conhecimento sobre SQL ou sobre o modelo relacional Para resolver esse problema, podemos desenvolver aplicações com interfaces que não exijam co- nhecimentos técnicos de SQL ou do modelo relacional para serem utilizadas. Dessa forma, usuários comuns poderiam manipular as informações do banco de dados através dessas aplicações. Nessa abordagem, os usuários interagem com as aplicações e as aplicações interagem com os SGDBs. www.k19.com.br Cadastro de Funcionários Nome: Código: Salário: Figura 2.2: Usuários comuns devem utilizar interfaces simples www.k19.com.br 23
ADO.NET 24 Driver As aplicações interagem com os SGDBs através de troca de mensagens. Os SGDBs definem o formato das mensagens. Para não sobrecarregar o canal de comunicação entre as aplicações e os SGDBs, as mensagens trocadas devem ocupar o menor espaço possível. Geralmente, protocolos binários são mais apropriados para reduzir o tamanho das mensagens e consequentemente diminuir a carga do canal de comunicação. Por isso, os SGDBs utilizam protocolos binários. rollback find getReference persist begin getTransaction commit 10110 111000 10010 Figura 2.3: Diminuindo o tamanho das mensagens para não sobrecarregar o meio de comunicação Mensagens binárias são facilmente interpretadas por computadores. Por outro lado, são com- plexas para um ser humano compreender. Dessa forma, o trabalho dos desenvolvedores seria muito complexo, aumentando o custo para o desenvolvimento e manutenção das aplicações. 10 010 11 110 01 1011010111 11 11 11 0 0010110011 0 0010110001 01 00 01 1101011101 10 111 10 110 11 01 01 1010111101 10 01 10 1 0101101001 0 01 01 00 11 0111011100 0111011100 01 101 11 0101101001 1010111101 1 010 1101011101 0010110001 0 0010110011 1011010111 Figura 2.4: Mensagens binárias são altamente complexas para os seres humanos Para resolver esse problema e facilitar o desenvolvimento das aplicações, as empresas proprietá- 24 www.k19.com.br
25 ADO.NET rias dos SGDBs, normalmente, desenvolvem e distribuem drivers de conexão. Um driver de conexão atua como um intermediário entre as aplicações e os SGDBs. Os drivers de conexão são “tradutores” de comandos escritos em uma determinada linguagem de programação para comandos definidos de acordo com o protocolo de um SGDB. Utilizando um driver de conexão, os desenvolvedores das aplicações não manipulam diretamente as mensagens binárias trocadas entre as aplicações e os SGDBs. Mais Sobre Em alguns casos, o protocolo binário de um determinado SGDB é fechado. Consequen- temente, a única maneira de se comunicar com ele é através de um driver de conexão oferecido pelo fabricante desse SGDB. ODBC Suponha que os proprietários dos bancos de dados desenvolvessem os drivers de maneira total- mente independente. Consequentemente, cada driver teria sua própria interface, ou seja, seu pró- prio conjunto de instruções. Dessa maneira, o desenvolvedor da aplicação precisa conhecer as ins- truções de cada um dos drivers dos respectivos bancos que ele for utilizar. Para facilitar o trabalho do desenvolvedor da aplicação, a Microsoft® definiu uma especificação chamada ODBC (Open Database Connectivity) para padronizar a interface dos drivers de conexão. Assim, quando uma empresa proprietária de um banco de dados pretende desenvolver um driver, ela segue essa especificação com o intuito de popularizá-lo. Os drivers de conexão que respeitam a especificação ODBC, ou seja, possuem um conjunto de comandos padronizados, são chamados de drivers de conexão ODBC. ODBC Manager Para que drivers ODBC possam ser instalados em uma máquina e as aplicações consigam utilizá-los é necessário ter o ODBC Manager, que já vem instalado no Windows®. O driver de conexão ODBC já está disponível para utilização, podemos consultar o ODBC Mana- ger do Windows®. O ODBC Manager pode ser executado através do item Ferramentas Administrati- vas do Painel de Controle. www.k19.com.br 25
ADO.NET 26 Criando uma conexão Com o driver de conexão ODBC instalado na máquina já é possível criar uma conexão com o banco de dados correspondente. O que é necessário para estabelecer uma conexão com o banco de dados? • Escolher o driver de conexão; • Definir a localização do banco de dados; • Informar o nome da base de dados; • Ter um usuário e senha cadastrados no banco de dados. Todas essas informações são definidas na chamada string de conexão. 1 string stringDeConexao = @ " driver ={ SQL Server }; 2 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; Código C# 2.1: Definindo a string de conexão Após a definição da string de conexão, podemos utilizar a classe System.Data.Odbc.OdbcConnec- tion do .NET Framework. Essa classe é responsável por criar conexões ODBC. 1 OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ; Código C# 2.2: Criando uma conexão ODBC Inserindo registros Estabelecida a conexão com o banco de dados, já podemos executar comandos. Por exemplo, é pos- sível inserir registros nas tabelas. O primeiro passo para executar um comando é defini-lo em lin- guagem SQL de acordo com a sintaxe do SGDB utilizado. 26 www.k19.com.br
27 ADO.NET 1 string textoDoComando = @ " INSERT INTO Editoras ( Nome , Email ) 2 VALUES ( ’ Abril ’ , ’ abril@email . com ’) ; " ; Mais Sobre O caractere “@” antes de um valor literal do tipo string indica que os caracteres dentro da string não devem ser processados como caracteres especiais. Em seguida, devemos criar um objeto da classe System. Data.Odbc.OdbcCommand a partir do código sql e da conexão previamente criados. O comando não é executado quando os objetos dessa classe são instanciados. 1 OdbcCommand comando = new OdbcCommand ( textoDoComando , conexao ) ; Código C# 2.4: Criando um comando ODBC Por fim, o comando pode ser executado através do método ExecuteNonQuery(). A conexão deve ser aberta antes de executar o comando. 1 conexao . Open () ; 2 comando . ExecuteNonQuery () ; Importante A mesma conexão pode ser reaproveitada para executar várias operações. Quando não houver mais operações a serem executadas, devemos finalizar a conexão ODBC através do método Close(). Finalizar as conexões ODBC que não são mais necessárias é importante pois libera recursos no SGBD. 1 conexao . close () ; Código C# 2.6: Finalizando uma conexão ODBC Mais Sobre Em C#, para evitar que o uma conexão não seja fechada após a sua utilização, podemos aplicar um bloco using. 1 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 2 { 3 // utiliza a conexao 4 } Código C# 2.7: Utilizando um bloco using No código acima, quando o bloco using que está associado à conexão ODBC terminar, automa- ticamente, essa conexão será fechada. Exercícios de Fixação www.k19.com.br 27
ADO.NET 28 1 Crie um projeto do tipo Console Application no Microsoft Visual C# Express, chamado ODBC. 2 Crie uma classe chamada InsereEditora no projeto ODBC para inserir registros na tabela Edi- toras. 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereEditora 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Nome da Editora : " ) ; 13 string nome = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Email da Editora : " ) ; 16 string email = System . Console . ReadLine () ; 17 18 string textoInsereEditora = 19 @ " INSERT INTO Editoras ( Nome , Email ) 20 VALUES ( ’ " + nome + @ " ’, ’" + email + @ " ’) " ; 21 22 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 23 { 24 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 25 conexao . Open () ; 26 command . ExecuteNonQuery () ; 27 } 28 } 29 } 30 } Código C# 2.8: InsereEditora.cs Exercícios Complementares 1 Crie uma classe chamada InsereLivro no projeto ODBC para inserir registros na tabela Livros. SQL Injection A implementação da inserção de registros feita anteriormente possui uma falha grave. Os dados ob- tidos do usuário através do teclado não são tratados antes de serem enviados para o SGDB. Esses dados podem conter caracteres especiais. Se esses caracteres não são tratados, o compor- tamento esperado da operação é afetado. Eventualmente, registros não são inseridos como deveriam ou brechas de segurança podem se abrir. Por exemplo, considere a classe InsereEditora do exercício de fixação. Se o usuário digitar “O’Reilly” e “oreilly@email.com”, o código SQL gerado pela aplicação seria: 1 INSERT INTO Editoras ( nome , email ) VALUES ( ’O ’ Reilly ’ , ’ oreilly@email . com ’) 28 www.k19.com.br
29 ADO.NET Observe que o caractere aspas simples aparece cinco vezes no código SQL acima. O SGDB não saberia dizer onde de fato termina o nome da editora. Ao tentar executar esse código, um erro de sintaxe é lançado pelo SQL Server. Para resolver esse problema manualmente, devemos adicionar dois caracteres “”’ seguidos. 1 INSERT INTO Editoras ( nome , email ) VALUES ( ’O ’ ’ Reilly ’ , ’ oreilly@email . com ’) Os valores recebidos dos usuários devem ser analisados e os caracteres especiais contidos nesses valores devem ser tratados. Esse processo é extremamente trabalhoso, pois o conjunto de caracteres especiais e a forma de tratá-los é diferente em cada SGDB. A responsabilidade do tratamento dos caracteres especiais contidos nos valores de entrada dos usuários pode ser repassada para os drivers ODBC. Dessa forma, o código das aplicações se torna independente das particularidades desse processo para cada SGDB. Mais Sobre O processo de tratamento dos caracteres especiais das entradas dos usuários é denomi- nado sanitize. 1 // pegando os dados da editora pelo teclado 2 string nome = System . Console . ReadLine () ; 3 string email = System . Console . ReadLine () ; 4 5 // definindo a sentença SQL com parâmetros 6 string textoDoComando = 7 @ " INSERT INTO Editoras ( Nome , Email ) VALUES (? , ?) ; " ; 8 9 // criando um comando odbc 10 OdbcCommand comando = new OdbcCommand ( textoDoComando , conexao ) ; 11 12 // atribuindo valores aos parâmetros 13 comando . Parameters . AddWithValue ( " @Nome " , nome ) ; 14 comando . Parameters . AddWithValue ( " @Email " , email ) ; Código C# 2.12: “Sanitizando” as entradas dos usuários Observe que a sentença SQL foi definida com parâmetros através do caractere “?”. Antes de exe- cutar o comando, é necessário atribuir valores aos parâmetros. Isso é feito com o método AddWith- Value(). Esse método realiza a tarefa de “sanitizar” os valores enviados pelo usuário. Exercícios de Fixação 3 Tente causar um erro de SQL Injection ao inserir editoras com a classe InsereEditora. (Dica: tente entradas com aspas simples) 4 Altere o código da classe InsereEditora para eliminar o problema do SQL Injection. Observe o código abaixo: 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereEditora www.k19.com.br 29
ADO.NET 30 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Nome da Editora : " ) ; 13 string nome = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Email da Editora : " ) ; 16 string email = System . Console . ReadLine () ; 17 18 string textoInsereEditora = 19 @ " INSERT INTO Editoras ( Nome , Email ) VALUES (? ,?) " ; 20 21 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 22 { 23 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 24 25 command . Parameters . AddWithValue ( " @Nome " , nome ) ; 26 command . Parameters . AddWithValue ( " @Email " , email ) ; 27 28 conexao . Open () ; 29 command . ExecuteNonQuery () ; 30 } 31 } 32 } 33 } Código C# 2.13: InsereEditora.cs 5 Agora tente causar novamente o problema de SQL Injection ao inserir novas editoras. Exercícios Complementares 2 Provoque um erro de SQL Injection na classe InsereLivro. (Dica: tente entradas com aspas simples.) 3 Altere o código para eliminar o problema do SQL Injection. 4 Agora tente causar novamente o problema de SQL Injection ao inserir novos livros. Listando registros Depois de inserir alguns registros, é interessante consultar os dados das tabelas para conferir se a in- serção foi realizada com sucesso. O processo para executar um comando de consulta é parecido com o de inserção. É necessário definir a sentença SQL e criar um objeto da classe OdbcCommand. 1 // definindo a sentença SQL 2 string textoDoComando = @ " SELECT * FROM Editoras ; " ; 3 4 // criando um comando odbc 5 OdbcCommand comando = new OdbcCommand ( textoDoComando , conexao ) ; Código C# 2.15: Criando um comando de seleção A diferença é que para executar um comando de consulta é necessário utilizar o método Execu- 30 www.k19.com.br
31 ADO.NET teReader() ao invés do ExecuteNonQuery(). Esse método devolve um objeto da classe System.Da- ta.Odbc.OdbcDataReader. 1 OdbcDataReader resultado = comando . ExecuteReader () ; Código C# 2.16: Executando um comando de consulta Os dados contidos no OdbcDataReader podem ser acessados através dos nomes das colunas. 1 string nome = resultado [ " Nome " ] as string ; 2 string email = resultado [ " Email " ] as string ; Código C# 2.17: Recuperando os campos do primeiro registro do resultado O código acima mostra como os campos do primeiro registro do resultado da consulta são recu- perados. Agora, para recuperar os outros registros é necessário avançar o OdbcDataReader através do método Read(). 1 string nome1 = resultado [ " nome " ] as string ; 2 string email1 = resultado [ " email " ] as string ; 3 4 resultado . Read () ; 5 6 string nome2 = resultado [ " nome " ] as string ; 7 string email2 = resultado [ " email " ] as string ; Código C# 2.18: Recuperando os campos dos dois primeiros registros do resultado O próprio método Read() devolve um valor booleano para indicar se o reader conseguiu avançar para o próximo registro. Quando esse método devolver false significa que não há mais registros para serem recuperados. 1 while ( resultado . Read () ) 2 { 3 string nome = resultado [ " nome " ] as string ; 4 string email = resultado [ " email " ] as string ; 5 } Código C# 2.19: Recuperando os campos de todos os registros do resultado Exercícios de Fixação 6 Insira algumas editoras utilizando a classe InsereEditora que você criou nos exercícios anteri- ores. 7 Adicione uma nova classe ao projeto ODBC chamada ListaEditora. O objetivo é listar as edi- toras que foram salvas no banco. Veja o código dessa classe. 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaEditora 6 { 7 static void Main ( string [] args ) 8 { www.k19.com.br 31
ADO.NET 32 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 13 { 14 string textoListaEditora = " SELECT * FROM Editoras " ; 15 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 16 conexao . Open () ; 17 OdbcDataReader resultado = command . ExecuteReader () ; 18 19 while ( resultado . Read () ) 20 { 21 long ? id = resultado [ " Id " ] as long ?; 22 string nome = resultado [ " Nome " ] as string ; 23 string email = resultado [ " Email " ] as string ; 24 System . Console . WriteLine ( " {0} : {1} - {2} n " ,id , nome , email ) ; 25 } 26 } 27 } 28 } 29 } Código C# 2.20: ListaEditora.cs Exercícios Complementares 5 Crie uma classe para listar os livros cadastrados na base de dados. Connection Factory Você deve ter percebido que para cada ação executada no banco de dados, nós precisamos criar uma conexão. Isso gera um problema relacionado à string de conexão ficar armazenada em diversos locais. Imagine que o driver do banco foi atualizado e mudamos a sua versão. Isso implicaria fa- zer diversas alterações no código em cada ocorrência da string de conexão, tornando o código mais suscetível a erros e dificultando a sua manutenção. Para resolver esta situação, nós poderíamos criar uma classe responsável pela criação e distri- buição de conexões, mantendo assim uma única referência para a string de conexão, e qualquer alteração no modo em que nos conectamos à base de dados, só implica mudanças nesta classe. 1 static class ConnectionFactory 2 { 3 public static OdbcConnection CreateConnection () 4 { 5 string driver = @ " SQL Server " ; 6 string servidor = @ " MARCELO - PC SQLEXPRESS " ; 7 string baseDeDados = @ " livraria " ; 8 string usuario = @ " sa " ; 9 string senha = @ " sa " ; 10 11 StringBuilder connectionString = new StringBuilder () ; 12 connectionString . Append ( " driver = " ) ; 13 connectionString . Append ( driver ) ; 14 connectionString . Append ( " ; server = " ) ; 15 connectionString . Append ( servidor ) ; 16 connectionString . Append ( " ; database = " ) ; 17 connectionString . Append ( baseDeDados ) ; 18 connectionString . Append ( " ; uid = " ) ; 32 www.k19.com.br
33 ADO.NET 19 connectionString . Append ( usuario ) ; 20 connectionString . Append ( " ; pwd = " ) ; 21 connectionString . Append ( senha ) ; 22 23 return new OdbcConnection ( connectionString . ToString () ) ; 24 } 25 } Código C# 2.22: ConnectionFactory.cs Agora podemos obter uma nova conexão apenas chamando ConnectionFactory.CreateConnection(). O resto do sistema não precisa mais conhecer os detalhes sobre a conexão com o banco de dados, diminuindo o acoplamento da aplicação. Exercícios de Fixação 8 Adicione uma nova classe chamada ConnectionFactory com seguinte código: 1 using System ; 2 using System . Data . Odbc ; 3 using System . Text ; 4 5 namespace Odbc 6 { 7 static class ConnectionFactory 8 { 9 public static OdbcConnection CreateConnection () 10 { 11 string driver = @ " SQL Server " ; 12 string servidor = @ " MARCELO - PC SQLEXPRESS " ; 13 string baseDeDados = @ " livraria " ; 14 string usuario = @ " sa " ; 15 string senha = @ " sa " ; 16 17 StringBuilder connectionString = new StringBuilder () ; 18 connectionString . Append ( " driver = " ) ; 19 connectionString . Append ( driver ) ; 20 connectionString . Append ( " ; server = " ) ; 21 connectionString . Append ( servidor ) ; 22 connectionString . Append ( " ; database = " ) ; 23 connectionString . Append ( baseDeDados ) ; 24 connectionString . Append ( " ; uid = " ) ; 25 connectionString . Append ( usuario ) ; 26 connectionString . Append ( " ; pwd = " ) ; 27 connectionString . Append ( senha ) ; 28 29 return new OdbcConnection ( connectionString . ToString () ) ; 30 } 31 } 32 } Código C# 2.23: ConnectionFactory.cs 9 Altere as classes InsereEditora e ListaEditora para que elas utilizem a fábrica de conexão. Execute-as novamente. 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereEditora 6 { www.k19.com.br 33
ADO.NET 34 7 static void Main ( string [] args ) 8 { 9 System . Console . Write ( " Digite o Nome da Editora : " ) ; 10 string nome = System . Console . ReadLine () ; 11 12 System . Console . Write ( " Digite o Email da Editora : " ) ; 13 string email = System . Console . ReadLine () ; 14 15 string textoInsereEditora = 16 @ " INSERT INTO Editoras ( Nome , Email ) VALUES (? ,?) " ; 17 18 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 19 { 20 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 21 22 command . Parameters . AddWithValue ( " @Nome " , nome ) ; 23 command . Parameters . AddWithValue ( " @Email " , email ) ; 24 25 conexao . Open () ; 26 command . ExecuteNonQuery () ; 27 } 28 } 29 } 30 } Código C# 2.24: InsereEditora.cs 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaEditora 6 { 7 static void Main ( string [] args ) 8 { 9 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 10 { 11 string textoListaEditora = " SELECT * FROM Editoras " ; 12 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 13 conexao . Open () ; 14 OdbcDataReader resultado = command . ExecuteReader () ; 15 16 while ( resultado . Read () ) 17 { 18 long ? id = resultado [ " Id " ] as long ?; 19 string nome = resultado [ " Nome " ] as string ; 20 string email = resultado [ " Email " ] as string ; 21 System . Console . WriteLine ( " {0} : {1} - {2} n " ,id , nome , email ) ; 22 } 23 } 24 } 25 } 26 } Código C# 2.25: ListaEditora.cs Exercícios Complementares 6 Altere as classes InsereLivro e ListaLivro para que elas utilizem a fábrica de conexão. Execute- as novamente. 34 www.k19.com.br
35 ADO.NET Desafios 1 Implemente um teste que remove uma editora pelo id. 2 Implemente um teste que altera os dados de uma editora pelo id. www.k19.com.br 35
ADO.NET 36 36 www.k19.com.br
CAPÍTULO E NTITY F RAMEWORK 3 Múltiplas sintaxes da linguagem SQL No capítulo anterior, utilizamos conexões ODBC para fazer uma aplicação C# interagir com os SGDBs. Nessa interação, as consultas são definidas com a linguagem SQL. A sintaxe dessa linguagem é diferente em cada SGDB. Dessa forma, a complexidade do trabalho dos desenvolvedores aumenta. Para resolver esse problema, as consultas deveriam ser definidas através de um mecanismo indepen- dente da linguagem SQL. SELECT TOP 100 * FROM livros ORDER BY autor ASC Driver SQL Server ODBC SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY autor ASC) AS rownumber, id, titulo, autor FROM livros ) WHERE rownumber <= 100 Driver Oracle ODBC Figura 3.1: Diferentes sintaxes da linguagem SQL Orientação a Objetos VS Modelo Relacional Outro problema na comunicação entre uma aplicação C# e um SGDB é o conflito de paradigmas. Os SGDBs são organizados seguindo o modelo relacional. Por outro lado, as aplicações C# utilizam www.k19.com.br 37
E NTITY F RAMEWORK 38 o modelo orientado a objetos. A transição de dados entre o modelo relacional e o modelo orientado a objetos não é simples. Para realizar essa transição, é necessário definir um mapeamento entre os conceitos desses dois pa- radigmas. Por exemplo, classes podem ser mapeadas para tabelas, objetos para registros, atributos para campos e referência entre objetos para chaves estrangeiras. tbl_editoras Livro id nome id = 1 1 Cultura titulo = Os Lusíadas 2 FTDA autor = Luís Vaz de Camões 3 Globo editora = 1 4 Scipione Editora id = 1 nome = Livraria Cultura tbl_livros Livro id = 2 id titulo autor editora_id titulo = Vidas Secas 1 Os Lusíadas Luís Vaz de Camões 1 autor = Graciliano Ramos 2 Vidas Secas Graciliano Ramos 1 editora = 1 3 Dom Casmurro Machado de Assis 3 4 O Cortiço Aluísio Azevedo 2 ORIENTAÇÃO A OBJETOS MODELO RELACIONAL Figura 3.2: Modelo Orientado a Objetos vs Modelo Relacional Ferramentas ORM Para facilitar a comunicação entre aplicações C# que seguem o modelo orientado a objetos e os SGDBs que seguem o modelo relacional, podemos utilizar ferramentas que automatizam a transição de dados entre as aplicações e os SGDBs. Essas ferramentas são conhecidas como ferramentas ORM (Object Relational Mapper). As ferramentas ORM oferecem mecanismos de consultas independentes da linguagem SQL. Dessa forma, o acoplamento entre as aplicações e os SGDBs diminui drasticamente. As principais ferra- mentas ORM para C# utilizadas no mercado são o Entity Framework e o NHibernate. 38 www.k19.com.br
39 E NTITY F RAMEWORK tbl_livros id titulo autor editora_id Livro Livro 1 Os Lusíadas Luís Vaz de Camões 1 FERRAMENTA id = 1 id = 2 2 Vidas Secas Graciliano Ramos 1 ORM titulo = Os Lusíadas autor = Luís Vaz de titulo = Vidas Secas autor = Graciliano 3 Dom Casmurro Machado de Assis 3 Camões Ramos 4 O Cortiço Aluísio Azevedo 2 editora = 1 editora = 1 Figura 3.3: Transformação dos dados do Modelo Relacional para o Modelo Orientado a Objetos tbl_livros Livro Livro id titulo autor editora_id id = 1 id = 2 FERRAMENTA 1 Os Lusíadas Luís Vaz de Camões 1 titulo = Os Lusíadas autor = Luís Vaz de titulo = Vidas Secas autor = Graciliano ORM Vidas Secas 2 Graciliano Ramos 1 Camões Ramos 3 Dom Casmurro Machado de Assis 3 editora = 1 editora = 1 4 O Cortiço Aluísio Azevedo 2 Figura 3.4: Transformação dos dados do Modelo Orientado a Objetos para o Modelo Relacional Nesse curso usaremos o Entity Framework 4.3.1, que pode ser obtido em http://www.k19.com. br/EntityFramework.dll ou http://nuget.org/packages/EntityFramework/4.3.1. Configuração Para configurar o Entity Framework em uma aplicação, podemos criar um arquivo chamado App.config. O conteúdo desse arquivo possuirá informações sobre o banco de dados, como a url de conexão, usuário e senha. Veja abaixo um exemplo de configuração para o App.config. 1 <? xml version = " 1.0 " ? > 2 < configuration > 3 < connectionStrings > 4 < add 5 name = " K19Context " 6 providerName = " System . Data . SqlClient " 7 connectionString = " Server =. SQLEXPRESS ; 8 Database = k19db ; 9 Trusted_Connection = false ; 10 User Id = sa ; 11 Password = sa ; 12 Persist Security Info = true ; 13 MultipleActiveResultSets = True " / > 14 </ connectionStrings > 15 </ configuration > Código XML 3.1: App.config www.k19.com.br 39
E NTITY F RAMEWORK 40 Mapeamento Um dos principais objetivos dos frameworks ORM é estabelecer o mapeamento entre os concei- tos do modelo orientado a objetos e os conceitos do modelo relacional. Este mapeamento pode ser definido através de um arquivo XML ou de maneira mais prática com DbContext. Quando utiliza- mos DbContext, evitamos a criação de extensos arquivos XML. Considere as entidades Livro e Editora definidas abaixo. 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 public string Titulo { get ; set ; } 5 public decimal Preco { get ; set ; } 6 public Editora Editora { get ; set ; } 7 } Código C# 3.1: Livro.cs 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public ICollection < Livro > Livros { get ; set ; } 7 } Código C# 3.2: Editora.cs Criaremos uma classe para ajudar a mapear essas entidades para o banco de dados. A classe EditoraContext deriva de DbContext, que faz parte da biblioteca Code First: 1 public class EditoraContext : DbContext 2 { 3 public DbSet < Editora > Editoras { get ; set ; } 4 public DbSet < Livro > Livros { get ; set ; } 5 } Código C# 3.3: EditoraContext.cs Utilizamos o recurso Code First do Entity Framework 4 para permitir a persistência no banco de dados. Isto significa que as propriedades Editoras e Livros de EditoraContext serão mapeadas para tabelas com mesmo nome no banco de dados. Ou seja, a propriedade Editoras será mapeada para uma tabela chamada Editoras e a propriedade Livros será mapeada para uma tabela chamada Livros. Cada propriedade definida nas entidades Livro e Editora será mapeada para uma coluna nas tabelas Livros e Editoras. Abaixo segue a definição da tabela Editoras que foi criada em nosso banco de dados: 40 www.k19.com.br
41 E NTITY F RAMEWORK Abaixo segue a definição da tabela Livros que foi criada em nosso banco de dados: Utilizando o Code First, não foi necessário configurar nada para que a persistência e o mapea- mento fossem realizados. Isto ocorreu simplesmente escrevendo as três classes acima. Nenhuma configuração a mais é necessária. Podemos utilizar anotações para sobrescrever o mapeamento padrão. Para utilizar anotações, precisamos adicionar como referência as bibliotecas EntityFramework.dll e System.Component- Model.DataAnnotations.dll ao projeto e acrescentar using para o namespace System.Component- Model.DataAnnotations. A seguir descrevemos as principais anotações. ColumnAttribute: Define o nome e o tipo da coluna no banco de dados da propriedade mapeada. 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 [ Column ( " NomeDoLivro " , TypeName = " varchar " ) ] 5 public string Titulo { get ; set ; } 6 public decimal Preco { get ; set ; } 7 public Editora Editora { get ; set ; } 8 } Código C# 3.4: Livro.cs DatabaseGeneratedAttribute: Utilizado para indicar que o valor do atributo é gerado automatica- mente pelo banco de dados. Para definir como o valor do atributo é gerado, podemos utilizar três constantes do enum DatabaseGenerateOption: • DatabaseGeneratedOption.Identity: o valor de um atributo com a opção Identity deve ser gerado quando a instância for salva (com a chamada de SaveChanges()) pela primeira vez. Além disso, o banco de dados supõe que esse valor não será mais alterado. Portanto, não se deve modificar o valor desse atributo. • DatabaseGeneratedOption.Computed: o valor de um atributo com a opção Computed é gerado sempre que a instância for salva. • DatabaseGeneratedOption.None: o valor de um atributo com a opção None não será ge- rado pelo banco de dados. ForeignKeyAttribute: Define que o atributo será mapeado para uma chave estrangeira. 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 public string Titulo { get ; set ; } www.k19.com.br 41
E NTITY F RAMEWORK 42 5 public decimal Preco { get ; set ; } 6 [ ForeignKey ( " Editora " ) ] 7 public int EditoraId { get ; set ; } 8 public Editora Editora { get ; set ; } 9 } Código C# 3.5: Livro.cs 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public ICollection < Livro > Livros { get ; set ; } 7 } Código C# 3.6: Editora.cs InversePropertyAttribute: Nos relacionamentos bidirecionais, utilizamos essa anotação para evitar o mapeamento de dois relacionamentos unidirecionais ao invés de um bidirecional. Por exemplo, considere as entidades Pessoa e Livro. Podemos estabelecer um relacionamento bidirecional entre essas entidades, pois uma pessoa pode escrever um livro. 1 public class Pessoa 2 { 3 public int Id { get ; set ; } 4 public string Nome { get ; set ; } 5 public ICollection < Livro > LivrosPublicados { get ; set ; } 6 } Código C# 3.7: Pessoa.cs 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 public string Titulo { get ; set ; } 5 public decimal Preco { get ; set ; } 6 public Editora Editora { get ; set ; } 7 8 [ InverseProperty ( " LivrosPublicados " ) ] 9 public Pessoa Autor { get ; set ; } 10 } Código C# 3.8: Livro.cs No código acima, a propriedade Autor da classe Livro foi mapeada com a anotação Inverse- Property para evitar que dois relacionamentos unidirecionais fossem estabelecidos entre Pes- soa e Livro. KeyAttribute: Define uma ou mais propriedades que identificam unicamente as instâncias de uma entidade. Se a classe define propriedades chamadas ID ou Id, ou com o nome da classe seguido por ID ou Id, esta propriedade é tratada como chave primária por convenção. Caso contrário, podemos definir a nossa chave primária com o KeyAttribute. No exemplo abaixo, definimos a propriedade Identificador para ser a chave primária correspondente à entidade Pessoa. 1 public class Pessoa 2 { 42 www.k19.com.br
43 E NTITY F RAMEWORK 3 [ Key ] 4 public int Identificador { get ; set ; } 5 public string Nome { get ; set ; } 6 public ICollection < Livro > LivrosPublicados { get ; set ; } 7 public ICollection < Livro > LivrosRevisados { get ; set ; } 8 } Código C# 3.9: Pessoa.cs MaxLengthAttribute: Define o tamanho máximo permitido para um array ou string. MinLengthAttribute: Define o tamanho mínimo permitido para um array ou string. 1 public class Pessoa 2 { 3 public int Id { get ; set ; } 4 [ MinLength (10 , ErrorMessage = " Tamanho minimo : 10 " ) ] 5 [ MaxLength (255 , ErrorMessage = " Tamanho máximo : 255 " ) ] 6 public string Nome { get ; set ; } 7 public ICollection < Livro > LivrosPublicados { get ; set ; } 8 public ICollection < Livro > LivrosRevisados { get ; set ; } 9 } Código C# 3.10: Pessoa.cs StringLengthAttribute: Define os tamanhos mínimo e máximo permitido para o campo string. 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 [ StringLength (255 , MinimumLength =10) ] 5 public string Nome { get ; set ; } 6 public string Email { get ; set ; } 7 public string ExtraInfo { get ; set ; } 8 public ICollection < Livro > Livros { get ; set ; } 9 } Código C# 3.11: Editora.cs NotMappedAttribute: Pode ser aplicado em classes ou propriedades. Indica que a classe ou propri- edade anotada não deve ser mapeada para o banco de dados. 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 [ NotMapped ] 7 public string ExtraInfo { get ; set ; } 8 public ICollection < Livro > Livros { get ; set ; } 9 } Código C# 3.12: Editora.cs RequiredAttribute: Define que o campo é obrigatório. Este atributo é ignorado em propriedades do tipo collection. Quando definido numa referência, indica que a cardinalidade é 1 e a proprie- dade da chave estrangeira é não-nula. 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 [ Required ] www.k19.com.br 43
E NTITY F RAMEWORK 44 5 public string Nome { get ; set ; } 6 public string Email { get ; set ; } 7 public string ExtraInfo { get ; set ; } 8 public ICollection < Livro > Livros { get ; set ; } 9 } Código C# 3.13: Editora.cs TableAttribute: Define a tabela para a qual a classe deve ser mapeada. 1 [ Table ( " Livros " ) ] 2 public class Livro 3 { 4 public int LivroId { get ; set ; } 5 public string Titulo { get ; set ; } 6 public decimal Preco { get ; set ; } 7 public Editora Editora { get ; set ; } 8 public Pessoa Autor { get ; set ; } 9 public Pessoa Revisor { get ; set ; } 10 } Código C# 3.14: Livro.cs Manipulando entidades Mostramos anteriormente que as entidades de uma aplicação são mapeadas através de uma classe que deriva de DbContext. Considere o seguinte exemplo. Suponha que as entidades da nossa aplicação foram mapeadas com a classe K19Context, que deriva de DbContext. As instâncias das entidades da nossa aplicação serão administradas por objetos da classe K19Context. Esses objetos são responsáveis pelas opera- ções de inserção, remoção, alteração e consulta. Mostraremos a seguir as principais operações que podem ser realizadas através de um DbContext. Persistindo Para indicar que determinado objeto deve ser persistido no banco de dados, devemos utilizar o método System.Data.Entity.DbSet.Add(), passando o objeto em questão como parâmetro. O método Add() adiciona o objeto ao contexto com o estado Added. Para armazenar de fato as informações de um objeto no banco de dados, utilizamos o método SaveChanges() do DbContext. Todos os objetos do contexto que estejam no estado Added são inse- ridos no banco de dados quando o método SaveChanges() é chamado. 1 using ( var context = new EditoraContext () ) 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 // Adiciona a editora criada ao contexto com estado Added 5 context . Editoras . Add ( editora ) ; 6 context . SaveChanges () ; 7 } Código C# 3.15: Persistindo um objeto 44 www.k19.com.br
45 E NTITY F RAMEWORK Buscando Para obter um objeto que contenha informações do banco de dados, basta utilizar o método Find() do DbSet. Podemos passar o identificador do objeto que desejamos recuperar como parâ- metro para esse método. 1 Editora editora = context . Editoras . Find (1) ; Código C# 3.16: Buscando uma editora com o identificador 1 Removendo Para indicar que determinado objeto deve ser removido, basta utilizar o método System.Da- ta.Entity.DbSet.Remove(). Esse método marca o objeto para ser removido do banco de dados, colocando-o no estado Deleted. Quando o método SaveChanges() é chamado, todas os objetos no estado Deleted são removidos do banco de dados. 1 Editora editora = context . Editoras . Find (1) ; 2 context . Editoras . Remove ( editora ) ; 3 context . SaveChanges () ; Código C# 3.17: Removendo uma editora do banco de dados Atualizando Para alterar os dados de um registro correspondente a um objeto, basta utilizar as suas proprieda- des. Quando as propriedades de um objeto do contexto são alteradas, o estado Modified é associado a este objeto. Objetos no estado Modified são atualizados no banco de dados quando o método SaveChanges() é chamado. 1 Editora editora = context . Editoras . Find (1) ; 2 editora . Nome = " Abril S / A " ; 3 context . SaveChanges () ; Código C# 3.18: Alterando o nome de uma editora Listando Para obter uma listagem com todos os objetos referentes aos registros de uma tabela, podemos utilizar o Language Integrated Query (LINQ), que nos permite escrever consultas dentro do código C#. 1 var consulta = from e in context . Editoras 2 where e . Nome . Contains ( " A " ) 3 select e ; 4 5 // Equivalente a : SELECT * FROM Editoras e where e . Nome Like ’A % ’ 6 foreach ( var item in consulta ) www.k19.com.br 45
E NTITY F RAMEWORK 46 7 { 8 System . Console . WriteLine ( " Editora : " + item . Nome ) ; 9 } Código C# 3.19: Utilizando LINQ para fazer uma consulta 46 www.k19.com.br
47 E NTITY F RAMEWORK Exercícios de Fixação 1 Crie um projeto do tipo Console Application no Microsoft Visual C# Express, chamado EF4-Code-First. 2 Adicione ao projeto as bibliotecas EntityFramework.dll e System.ComponentModel.Data- Annotations.dll. Figura 3.5: Adicionando uma referência Faça o download da biblioteca EntityFramework.dll no site da K19 (http://www.k19.com.br/ EntityFramework.dll) e então adicione-a ao projeto: Figura 3.6: Adicionando EntityFramework.dll ao projeto www.k19.com.br 47
E NTITY F RAMEWORK 48 Adicione a biblioteca System.ComponentModel.DataAnnotations.dll ao projeto: Figura 3.7: Adicionando System.ComponentModel.DataAnnotations.dll ao projeto 3 Defina uma classe Editora conforme código abaixo: 1 namespace EF4_Code_First 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 } 9 } Código C# 3.20: Editora.cs 4 Defina uma classe LivrariaContext que é derivada de DbContext. Nesta classe defina uma propriedade Editoras do tipo genérico DbSet<Editora>. 1 using System . Data . Entity ; 2 3 namespace EF4_Code_First 4 { 5 public class LivrariaContext : DbContext 6 { 7 public DbSet < Editora > Editoras { get ; set ; } 8 } 9 } Código C# 3.21: LivrariaContext.cs 5 Adicione o arquivo App.config e defina a string de conexão. 48 www.k19.com.br
49 E NTITY F RAMEWORK Figura 3.8: App.config 1 <? xml version = " 1.0 " encoding = " utf -8 " ? > 2 < configuration > 3 < connectionStrings > 4 < add 5 name = " LivrariaContext " 6 providerName = " System . Data . SqlClient " 7 connectionString = " Server =. SQLEXPRESS ; 8 Database = efcodefirst ; 9 Trusted_Connection = false ; 10 User Id = sa ; 11 Password = sa ; 12 Persist Security Info = true ; 13 MultipleActiveResultSets = True " / > 14 </ connectionStrings > 15 </ configuration > Código XHTML 3.1: Arquivo App.config 6 Defina uma classe InsereEditora: 1 using System ; 2 3 namespace EF4_Code_First 4 { 5 public class InsereEditora 6 { 7 static void Main ( string [] args ) 8 { 9 using ( LivrariaContext db = new LivrariaContext () ) 10 { 11 Console . WriteLine ( " Digite o nome da Editora : " ) ; 12 string nome = Console . ReadLine () ; 13 Console . WriteLine ( " Digite o email da Editora : " ) ; 14 string email = Console . ReadLine () ; 15 Editora e = new Editora { Nome = nome , Email = email }; 16 db . Editoras . Add ( e ) ; www.k19.com.br 49
E NTITY F RAMEWORK 50 17 db . SaveChanges () ; 18 } 19 } 20 } 21 } Código C# 3.22: InsereEditora.cs 7 Defina a classe ListaEditoras: 1 using System . Linq ; 2 using System ; 3 4 namespace EF4_Code_First 5 { 6 public class ListaEditoras 7 { 8 static void Main ( string [] args ) 9 { 10 using ( LivrariaContext db = new LivrariaContext () ) 11 { 12 var consulta = from e in db . Editoras 13 select e ; 14 foreach ( Editora editora in consulta ) 15 { 16 Console . WriteLine ( " {0} - {1} {2} " , editora . Id , editora . Nome , editora . Email ) ; 17 } 18 } 19 } 20 } 21 } Código C# 3.23: classe ListaEditoras Repositórios As classes DbContext e DbSet do EF oferecem recursos suficientes para que os objetos do domí- nio sejam persistidos, recuperados, alterados e removidos do banco de dados. Porém, em aplicações com alta complexidade e grande quantidade de código, “espalhar” as chamadas aos métodos do DbContext e DbSet pode gerar dificuldades na manutenção e no entendimento do sistema. Para melhorar a organização das nossas aplicações, diminuindo o custo de manutenção e au- mentando a legibilidade do código, podemos aplicar o padrão Repository do DDD (Domain Driven Design). Conceitualmente, um repositório representa o conjunto de todos os objetos de um determinado tipo. Ele deve oferecer métodos para administrar seus elementos. Os repositórios podem trabalhar com objetos prontos na memória ou reconstruí-los com dados obtidos de um banco de dados. O acesso ao banco de dados pode ser realizado através de ferramenta ORM como o Entity Framework. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Data . Entity ; 5 6 namespace EF4_Code_First 7 { 50 www.k19.com.br
51 E NTITY F RAMEWORK 8 public class EditoraRepository 9 { 10 DbContext context ; 11 12 public EditoraRepository ( DbContext context ) 13 { 14 this . context = context ; 15 } 16 17 public void Adiciona ( Editora e ) 18 { 19 context . Set < Editora >() . Add ( e ) ; 20 context . SaveChanges () ; 21 } 22 23 public Editora Busca ( int id ) 24 { 25 return context . Set < Editora >() . Find ( id ) ; 26 } 27 28 public List < Editora > BuscaTodas () 29 { 30 var consulta = from e in context . Set < Editora >() 31 select e ; 32 return consulta . ToList () ; 33 } 34 } 35 } Código C# 3.24: EditoraRepository.cs 1 var context = new EditoraContext () ; 2 3 EditoraRepository repository = new EditoraRepository ( context ) ; 4 5 List < Editora > editoras = repository . BuscaTodas () ; Código C# 3.25: Buscando todas as editoras armazenadas no banco de dados Exercícios de Fixação 8 Implemente um repositório de editoras criando uma nova classe no projeto EF4-Code-First. 1 using System ; 2 using System . Linq ; 3 using System . Collections . Generic ; 4 5 namespace EF4_Code_First 6 { 7 public class EditoraRepository : IDisposable 8 { 9 private LivrariaContext db = new LivrariaContext () ; 10 11 public void Adiciona ( Editora e ) 12 { 13 db . Editoras . Add ( e ) ; 14 db . SaveChanges () ; 15 } 16 17 public Editora Busca ( int id ) 18 { 19 Editora e = db . Editoras . Find ( id ) ; 20 return e ; 21 } www.k19.com.br 51
E NTITY F RAMEWORK 52 22 23 public List < Editora > BuscaTodas () 24 { 25 var consulta = from e in db . Editoras 26 select e ; 27 return consulta . ToList () ; 28 } 29 30 public void Dispose () 31 { 32 db . Dispose () ; 33 } 34 } 35 } Código C# 3.26: EditoraRepository.cs 9 Altere a classe InsereEditora para que utilize o repositório de editoras. 1 using System ; 2 3 namespace EF4_Code_First 4 { 5 public class InsereEditora 6 { 7 static void Main ( string [] args ) 8 { 9 using ( EditoraRepository repository = new EditoraRepository () ) { 10 Console . WriteLine ( " Digite o nome da Editora : " ) ; 11 string nome = Console . ReadLine () ; 12 Console . WriteLine ( " Digite o email da Editora : " ) ; 13 string email = Console . ReadLine () ; 14 Editora e = new Editora { Nome = nome , Email = email }; 15 repository . Adiciona ( e ) ; 16 } 17 } 18 } 19 } Código C# 3.27: InsereEditora.cs 10 Altere a classe ListaEditoras para que utilize o repositório de editoras. 1 using System ; 2 3 namespace EF4_Code_First 4 { 5 public class ListaEditoras 6 { 7 static void Main ( string [] args ) 8 { 9 using ( EditoraRepository repository = new EditoraRepository () ) { 10 var consulta = repository . BuscaTodas () ; 11 foreach ( Editora editora in consulta ) 12 { 13 Console . WriteLine ( " {0} - {1} {2} " , editora . Id , editora . Nome , editora . Email ) ; 14 } 15 } 16 } 17 } 18 } Código C# 3.28: ListaEditoras.cs 52 www.k19.com.br
CAPÍTULO V ISÃO G ERAL DO ASP.NET MVC 4 Necessidades de uma aplicação web HTTP Os usuários de uma aplicação web utilizam navegadores (browsers) para interagir com essa apli- cação. A comunicação entre navegadores e uma aplicação web é realizada através de requisições e respostas definidas pelo protocolo HTTP Dessa forma, os desenvolvedores de aplicações web devem . estar preparados para trabalhar com o protocolo HTTP. Acesso simultâneo Além disso, na grande maioria dos casos, as aplicações web devem ser acessadas por diversos usuários ao mesmo tempo. Consequentemente, os desenvolvedores web devem criar ou utilizar algum mecanismo eficiente que permita esse tipo de acesso. Conteúdo dinâmico As páginas de uma aplicação web devem ser geradas dinamicamente. Por exemplo, quando um usuário de uma aplicação de email acessa a sua caixa de entrada, ele deseja ver todos os emails enviados até aquele momento. A página contendo a lista de emails deve ser gerada novamente toda vez que essa página for requisitada. Consequentemente, os desenvolvedores web devem criar ou utilizar algum mecanismo eficiente que permita que o conteúdo das páginas das aplicações web seja gerado dinamicamente. www.k19.com.br 53
V ISÃO G ERAL DO ASP.NET MVC 54 Aplicação Web TP Re HT sp Requisição HTTP Resposta HTTP o ão st ç a isi HT qu TP Re TP Re HT sp o o st çã aH isi qu TT Re P www.k19.com.br www.k19.com.br www.k19.com.br Cursos Artigos Apostilas Figura 4.1: Necessidades de uma aplicação web Solução Resolver os três problemas apresentados tomaria boa parte do tempo de desenvolvimento, além de exigir conhecimentos técnicos extremamente específicos por parte dos desenvolvedores. Para facilitar o desenvolvimento de aplicações web, a plataforma .NET soluciona esses problemas funda- mentais. A plataforma .NET oferece os seguintes recursos fundamentais para o desenvolvimento de apli- cações web: • Envio e recebimento de mensagens HTTP. • Acesso simultâneo das aplicações por vários usuários de uma maneira eficiente. • Geração dinâmica das páginas das aplicações. ASP.NET MVC Atualmente, o ASP.NET MVC é o framework para desenvolvimento de aplicações web na pla- taforma .NET em maior ascensão. A documentação desse framework pode ser obtida em http: //www.asp.net/mvc. O ASP.NET MVC é fortemente baseado nos padrões MVC e Front Controller. MVC e Front Controller O MVC (model-view-controller) é um padrão de arquitetura que tem por objetivo isolar a lógica de negócio da lógica de apresentação de uma aplicação. 54 www.k19.com.br
55 V ISÃO G ERAL DO ASP.NET MVC Esse padrão (ou alguma variação) é amplamente adotado nas principais plataformas de desen- volvimento atuais. Em particular, ele é bastante utilizado no desenvolvimento de aplicações web. O padrão MVC divide uma aplicação em três tipos de componentes: modelo, visão e controlador. Modelo: encapsula os dados e as funcionalidades da aplicação. Visão: é responsável pela exibição de informações, cujos dados são obtidos do modelo. Controlador: recebe as requisições do usuário e aciona o modelo e/ou a visão. Para mais detalhes sobre o padrão MVC, uma boa referência é o livro Pattern-Oriented Software Architecture Volume 1: A System of Patterns (editora Wiley, 1996) dos autores Frank Buschmann, Re- gine Meunier, Hans Rohnert, Peter Sommerlad, Michael Stal e Michael Stal. No padrão Front Controller, todas as requisições do usuário são recebidas pelo mesmo compo- nente. Dessa forma, tarefas que devem ser realizadas em todas as requisições podem ser implemen- tadas por esse componente. Isso evita a repetição de código e facilita a manutenção do sistema. Para mais informações sobre esse padrão, consulte, por exemplo, o livro Professional ASP.NET Design Patterns (editora Wrox, 720) de Scott Millett. Visual Web Developer O primeiro passo para construir uma aplicação web utilizando ASP .NET MVC é criar um projeto no Visual Web Developer a partir do modelo adequado. No nosso caso, o modelo de projeto que deve ser utilizado é o ASP.NET MVC 4 Web Application. Figura 4.2: Criando um projeto www.k19.com.br 55
V ISÃO G ERAL DO ASP.NET MVC 56 Devemos escolher Basic Project conforme figura abaixo: Figura 4.3: Criando um projeto O projeto criado já vem com diversas pastas e arquivos. Ao longo dos próximos capítulos, a fun- ção de cada pasta e de cada arquivo será discutida. Testando a aplicação Para verificar o funcionamento do projeto, basta executá-lo através do menu: Debug -> Start De- bugging. Automaticamente um servidor de desenvolvimento é inicializado na máquina e a aplicação é implantada nesse servidor. Além disso, uma janela do navegador padrão do sistema é aberta na url principal da aplicação. O servidor pode ser finalizado através do ícone ASP.NET Development Server que fica na barra de tarefas do Windows. Trocando a porta do servidor Para trocar a porta utilizada pelo servidor de desenvolvimento que o Visual Web Developer uti- liza, basta alterar as propriedades do projeto clicando com o botão direito do mouse no projeto e escolhendo o item properties e depois a aba web. Exemplo de uma Aplicação Web 56 www.k19.com.br
57 V ISÃO G ERAL DO ASP.NET MVC Veremos a seguir um exemplo de uma simples aplicação web em ASP.NET MVC. Essa aplicação terá apenas uma página web que mostrará o número de acessos à essa página até o momento. Primeiramente, criaremos uma classe na camada de controle da aplicação para armazenar esse número. As classes que definem controladores devem derivar da classe Controller e seus nomes de- vem terminar com a palavra Controller. Além disso, essas classes devem estar na pasta Controllers da aplicação. Considere a classe K19Controller definida abaixo. 1 public class K19Controller : Controller 2 { 3 private static int NumeroDeAcessos { get ; set ; } 4 5 public ActionResult Home () 6 { 7 K19Controller . NumeroDeAcessos ++; 8 ViewBag . Acessos = K19Controller . NumeroDeAcessos ; 9 return View () ; 10 } 11 } Código C# 4.1: K19Controller.cs A classe K19Controller possui uma propriedade chamada NumeroDeAcessos, que armazenará o número de acessos à nossa página. O método Home() será chamado toda vez que a url http://<IP_SERVIDOR>:<PORTA_APP>/K19/Home for requisitada por um navegador. Note que essa url é formada pelo nome do controlador (nome da classe que define o controlador sem o sufixo Controller) seguido pelo nome desse método. Cada vez que o método Home() é chamado, o valor da propriedade NumeroDeAcessos é incre- mentado. Além disso, o valor atualizado dessa propriedade é colocado na ViewBag para que possa ser acessado na camada de apresentação. Para indicar que o fluxo de execução deve seguir para a camada de apresentação, o método Home() invoca o método View() e devolve a resposta obtida. Por padrão, o fluxo será direcionado para um arquivo chamado Home.cshtml que deve estar localizado na pasta ViewsK19. Na camada de apresentação, vamos adicionar a tela associada ao método Home(). Para isso, cria- remos um arquivo chamado Home.cshtml na pasta ViewsK19 da aplicação com o conteúdo abaixo. 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 < h2 > Acessos : @ViewBag . Acessos </ h2 > Código CSHTML 4.1: Home.cshtml Para acessar a ViewBag, devemos utilizar o caractere @, como no código acima. Exercícios de Fixação 1 Crie um projeto do tipo ASP.NET MVC 4 Web Application chamado K19. Utilize o template Basic www.k19.com.br 57
V ISÃO G ERAL DO ASP.NET MVC 58 Project. Figura 4.4: Criando um projeto Figura 4.5: Criando um projeto 2 Defina uma página de saudação. Para isto, crie um controlador chamado K19 no projeto K19 conforme a figura abaixo: 58 www.k19.com.br
59 V ISÃO G ERAL DO ASP.NET MVC Figura 4.6: Criando um controlador Figura 4.7: Criando um controlador 1 using System ; 2 using System . Web . Mvc ; 3 4 namespace K19 . Controllers 5 { 6 public class K19Controller : Controller 7 { 8 public ActionResult Home () 9 { 10 Random random = new Random () ; 11 ViewBag . NumeroDaSorte = random . Next () ; 12 return View () ; 13 } 14 } 15 } Código C# 4.2: K19Controller.cs www.k19.com.br 59
V ISÃO G ERAL DO ASP.NET MVC 60 3 Dentro da pasta Views, crie uma pasta chamada K19. Figura 4.8: Criando uma pasta 4 Defina a página com a mensagem de saudação. Crie um arquivo chamado Home.cshtml dentro da pasta ViewsK19. Figura 4.9: Adicionando uma tela 60 www.k19.com.br
61 V ISÃO G ERAL DO ASP.NET MVC Figura 4.10: Adicionando uma tela 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 < h2 > Olá ! O seu número da sorte é @ViewBag . NumeroDaSorte </ h2 > Código CSHTML 4.2: Home.cshtml 5 Para visualizar a página, basta executar o projeto no Visual Studio e acessar o endereço http://localhost:<PORTA_APP>/K19/Home www.k19.com.br 61
V ISÃO G ERAL DO ASP.NET MVC 62 Figura 4.11: Acessando a página da aplicação Integração com Entity Framework A integração entre Entity Framework e ASP.NET MVC 4 é realizada de maneira extremamente simples. No Visual Studio 2010, ao criar-se um projeto ASP.NET MVC 4 do tipo Basic, a biblioteca do Entity Framework 5 fica disponível automaticamente para o projeto. A string de conexão que a aplicação ASP.NET MVC 4 utilizará deve ser definida no arquivo de configuração Web.config. 1 ... 2 < connectionStrings > 3 < add 4 name = " K19Context " 5 providerName = " System . Data . SqlClient " 6 connectionString = " Server =. SQLEXPRESS ; 7 Database = k19bd ; 8 Trusted_Connection = false ; 9 User Id = sa ; 10 Password = sa ; 11 Persist Security Info = true ; 12 MultipleActiveResultSets = True " / > 13 </ connectionStrings > 14 ... Código XML 4.1: Exemplo de string de conexão Por fim, bastaria implementar as entidades e mapeá-las como visto no Capítulo 3. Scaffold O Visual Studio 2010 é capaz de gerar telas e controladores para as operações de inserção, leitura, alteração e remoção (CRUD) a partir de uma entidade de um projeto ASP.NET MVC 4. Os controla- 62 www.k19.com.br
63 V ISÃO G ERAL DO ASP.NET MVC dores gerados podem utilizar as funcionalidades do Entity Framework para interagir com o banco de dados. Exercícios de Fixação 6 Vamos gerar o nosso primeiro scaffold. Para isto, defina as classes Editora e K19Context na pasta Models do nosso projeto K19. 1 namespace K19 . Models 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 } 9 } Código C# 4.3: Editora.cs 1 using System . Data . Entity ; 2 3 namespace K19 . Models 4 { 5 public class K19Context : DbContext 6 { 7 public DbSet < Editora > Editoras { get ; set ; } 8 } 9 } Código C# 4.4: K19Context.cs 7 Defina a string de conexão. Para isto, altere o arquivo Web.config da raiz do projeto K19 e acrescente a string de conexão dentro da tag <configuration>. 1 ... 2 < connectionStrings > 3 < add 4 name = " K19Context " 5 providerName = " System . Data . SqlClient " 6 connectionString = " Server =. SQLEXPRESS ; 7 Database = k19bd ; 8 Trusted_Connection = false ; 9 User Id = sa ; 10 Password = sa ; 11 Persist Security Info = true ; 12 MultipleActiveResultSets = True " / > 13 </ connectionStrings > 14 ... Código XML 4.2: String de conexão 8 Antes de gerar o scaffold, precisamos fazer um build no projeto. www.k19.com.br 63
V ISÃO G ERAL DO ASP.NET MVC 64 Figura 4.12: Build do projeto 9 Após o build do projeto, podemos gerar o scaffold. Para isto, devemos criar um controlador chamado Editora conforme a figura abaixo. Figura 4.13: Gerando o scaffold Defina o model que deseja gerar o scaffold e a classe responsável pelo acesso ao banco de dados. 64 www.k19.com.br
65 V ISÃO G ERAL DO ASP.NET MVC Figura 4.14: Gerando o scaffold Após gerar o scaffold, verifique os arquivos gerados no projeto. www.k19.com.br 65
V ISÃO G ERAL DO ASP.NET MVC 66 Figura 4.15: Scaffold gerado 10 Para testar, acesse o endereço http://localhost:<PORTA_APP>/Editora. 66 www.k19.com.br
CAPÍTULO C AMADA DE A PRESENTAÇÃO 5 A camada de apresentação é responsável por gerar as páginas de uma aplicação web. Os dados apresentados em uma página web são definidos na camada de modelo. A camada de controle re- cupera os dados da camada de modelo e os envia para a camada de apresentação. Basicamente, a camada de apresentação definirá como esses dados serão apresentados para os usuários da aplica- ção. O fluxo inverso também ocorre. Ou seja, a camada de apresentação obtém dados inseridos pelos usuários e os envia para a camada de controle que, por sua vez, usa esses dados para alterar a camada de modelo. Mostraremos, neste capítulo, como a camada de apresentação de uma aplicação web é cons- truída com a utilização de recursos do ASP.NET MVC como Inline Code, HTML Helper, Layout e Par- tial Views. Razor e ASPX Até a segunda versão do ASP .NET MVC, as telas (ou páginas) de uma aplicação web eram de- senvolvidas em ASPX. A partir da terceira versão desse framework, podemos usar a linguagem Razor para construir essas telas. A principal característica da Razor é ser concisa e simples, diminuindo o número de caracteres e as tradicionais tags de scripts do ASP.NET MVC (<% %>). Nos exemplos abaixo, mostraremos algumas diferença entre Razor e ASPX. Criando Variáveis Toda expressão em Razor começa com o caractere @. Para criarmos variáveis, precisamos declará- las dentro de um bloco Razor: 1 @{ 2 string nome = " K19 " ; 3 string telefoneDaK19 = " 2387 -3791 " ; 4 string enderecoDaK19 = " Av . Brigadeiro Faria Lima " ; 5 int numeroDaK19 = 1571; 6 } Código CSHTML 5.1: Criando variáveis em Razor 1 <% 2 string nome = " K19 " ; 3 string telefoneDaK19 = " 2387 -3791 " ; 4 string enderecoDaK19 = " Av . Brigadeiro Faria Lima " ; 5 int numeroDaK19 = 1571; 6 %> www.k19.com.br 67
C AMADA DE A PRESENTAÇÃO 68 Código ASPX 5.1: Criando variáveis em ASPX Acessando variáveis As variáveis podem ser facilmente acessadas. 1 <p > Telefone da K19 : @telefoneDaK19 </ p > Código CSHTML 5.2: Acessando uma variável 1 <p > Telefone da K19 : < %= telefoneDaK19 % > </ p > Código ASPX 5.2: Acessando uma variável Condicionais (if e else) Podemos utilizar os comandos de controle de fluxo if e else. Veja os exemplos abaixo. 1 @if ( numero == numeroDaK19 ) 2 { 3 <p > Chegou na K19 ! </ p > 4 } 5 else 6 { 7 <p > Continue andando . </ p > 8 } 9 <! -- ou -- > 10 @{ 11 int numero = 463; 12 if ( numero == numeroDaK19 ) 13 { 14 <p > Chegou na K19 ! </ p > 15 } 16 else 17 { 18 <p > Continue andando . </ p > 19 } 20 } Código CSHTML 5.3: Utilizando if e else em Razor 1 <% 2 if ( numero == numeroDaK19 ) 3 { 4 %> 5 <p > Chegou na K19 ! </ p > 6 <% 7 } 8 else 9 { 10 %> 11 <p > Continue andando . </ p > 12 <% 13 } 14 %> Código CSHTML 5.4: Utilizando if e else em ASPX 68 www.k19.com.br
69 C AMADA DE A PRESENTAÇÃO Laços Podemos criar laços utilizando o comando for. 1 @for ( int i = 0; i < 5; i ++) 2 { 3 <p >i = @i </ p > 4 } Código CSHTML 5.5: Criando um laço em Razor 1 <% 2 for ( int i = 0; i < 5; i ++) 3 { 4 %> 5 <p >i = < %= i % > </ p > 6 <% 7 } 8 %> Código ASPX 5.3: Criando um laço em ASPX Texto e código 1 @if ( x == " nome " ) 2 { 3 @ : O nome da editora é @editora . Nome 4 } Código CSHTML 5.6: Texto e código em Razor 1 <% 2 if ( x == " nome " ) 3 { 4 %> 5 O nome da editora é < %= editora . Nome % > 6 <% 7 } 8 %> Código ASPX 5.4: Texto e código em ASPX Comentários Podemos adicionar comentários nas páginas. 1 @ * Comentário * @ Código CSHTML 5.7: Comentários em Razor 1 <% -- Comentário --% > Código ASPX 5.5: Comentários em ASPX www.k19.com.br 69
C AMADA DE A PRESENTAÇÃO 70 Exercícios de Fixação 1 Utilize o projeto K19 e altere o arquivo ViewsK19Home.cshtml para imprimir uma mensagem dez vezes. 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 5 @for ( int i = 0; i < 10; i ++) 6 { 7 < h2 > Olá ! O seu número da sorte é @ViewBag . NumeroDaSorte </ h2 > 8 } Código CSHTML 5.8: Home.cshtml Para ver o resultado, acesse a url http://localhost:<PORTA_APP>/K19/Home. 2 Altere o arquivo Home.cshtml do exercício anterior para imprimir uma mensagem a partir de uma variável. 1 @{ 2 ViewBag . Title = " Home " ; 3 string mensagem = " Olá ! O seu número da sorte é " + @ViewBag . NumeroDaSorte ; 4 } 5 6 @for ( int i = 0; i < 10; i ++) 7 { 8 < h2 > @mensagem </ h2 > 9 } Código CSHTML 5.9: Home.cshtml Para ver o resultado, acesse a url http://localhost:<PORTA_APP>/K19/Home. Exercícios Complementares 1 Altere o arquivo Home.cshtml para gerar a seguinte tela para o usuário: 70 www.k19.com.br
71 C AMADA DE A PRESENTAÇÃO Figura 5.1: Inline Code ViewBag e Strogly Typed Views A ViewBag é utilizada para transmitir dados da camada de controle para a camada de apresenta- ção. Para adicionar um item à propriedade ViewBag, devemos definir uma chave para esse item. Veja o código abaixo. 1 ViewBag . HoraDoServidor = DateTime . Now . ToShortTimeString () ; Código C# 5.1: Adicionando um item na ViewBag No código acima, criamos uma chave chamada HoraDoServidor e atribuímos um valor a essa chave. Na camada de apresentação, os itens armazenados na ViewBag podem ser acessados facilmente através da sintaxe da Razor. Veja um exemplo abaixo. 1 A hora do servidor é @ViewBag . HoraDoServidor Código CSHTML 5.11: Acessando um item da ViewBag A ViewBag oferece uma forma simples de transmitir dados da camada de controle para a camada de apresentação, mas apresenta algumas desvantagens. Por exemplo, como as chaves são adicio- nadas dinamicamente, não é possível garantir a existência dessas chaves em tempo de compilação. Considere o código abaixo. www.k19.com.br 71
C AMADA DE A PRESENTAÇÃO 72 1 double numero = new Random () . NextDouble () ; 2 if ( numero < 0.5) 3 { 4 ViewBag . X = " K19 TREINAMENTOS " ; 5 } 6 else 7 { 8 ViewBag . X = 10; 9 } Código C# 5.2: Adcicionando chaves dinamicamente na ViewBag No código acima, o tipo do objeto associado à chave X depende do valor da variável numero, que é gerado aleatoriamente. Quando o valor gerado for menor do que 0.5, o tipo desse objeto será string. Caso contrário, o tipo desse objeto será int. Suponha que a chave X seja utilizada na camada de apresentação. 1 <p > @ViewBag . X . Lower () </ p > Código CSHTML 5.12: Utilizando a chave X ViewBag O código acima não produz erros de compilação. Contudo, na execução, um erro poderá ocorrer. O problema é que esse código supõe que o objeto associado à chave X possui o método Lower(). No entanto, se a ação for acionada um número suficiente de vezes, em algum momento isso não será verdade, e um erro de execução ocorrerá. Além disso, recursos do Visual Studio como IntelliSense (recurso para completar código) ou refa- toração não podem ser aplicados com o uso da ViewBag. Uma alternativa à utilização da ViewBag é o recurso strongly typed views. Com esse recurso podemos fixar, em tempo de compilação, o tipo do objeto que será transferido da camada de controle para a camada de apresentação. Uma vez que o tipo desse objeto foi fixado, o compilador é capaz de verificar a validade do código, evitando eventuais erros de execução. Além disso, o Visual Studio pode auxiliar o desenvolvimento da aplicação com os recursos de IntelliSense e de refatoração. Para utilizar strongly type views, o primeiro passo é, na camada de controle, passar como parâ- metro para o método View() um objeto do tipo esperado pela camada de apresentação. 1 public ActionResult acao () 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View ( editora ) ; 5 } Código C# 5.3: Passando um objeto para a camada de apresentação Para definir o tipo do objeto esperado na camada de apresentação, podemos utilizar a sintaxe da Razor. Veja o código abaixo. 1 @model K19 . Models . Editora Código CSHTML 5.13: Definindo o tipo do objeto esperado na camada de apresentação Podemos também utilizar o comando using. 1 @using K19 . Models 72 www.k19.com.br
73 C AMADA DE A PRESENTAÇÃO 2 @model Editora Código CSHTML 5.14: Utilizando o comando using Para acessar o objeto transmitido da camada de controle para a camada de apresentação, deve- mos utilizar a propriedade Model. Observe o código a seguir. 1 @using K19 . Models 2 @model Editora 3 4 <p > 5 Nome : @Model . Nome 6 < br / > 7 Email : @Model . Email 8 </ p > Código CSHTML 5.15: Utilizando a propriedade Model Se tentarmos acessar um método ou propriedade inexistentes, um erro de compilação ocorrerá, pois agora o compilador conhece o tipo do objeto transmitido. Dessa forma, o código abaixo produz um erro de compilação. 1 @using K19 . Models 2 @model Editora 3 4 <p > 5 Nome : @Model . Nome 6 < br / > 7 Telefone : @Model . Telefone 8 </ p > Código CSHTML 5.16: Produzindo um erro de compilação Observe que apenas um objeto pode ser transmitido da camada de controle para a camada de apresentação através do recurso strongly type views. Já com a utilização de ViewBag, diversos obje- tos podem ser transmitidos. Note também, que ambos os recursos podem ser utilizados ao mesmo tempo. Exercícios de Fixação 3 Crie uma página que mostra a hora do servidor. No projeto K19, defina um controlador chamado Teste vazio(empty) e uma ação chamada TestaViewBag. Essa ação deve armazenar o horário atual na ViewBag. 1 using System ; 2 using System . Web . Mvc ; 3 4 namespace K19 . Controllers 5 { 6 public class TesteController : Controller 7 { 8 public ActionResult TestaViewBag () 9 { 10 ViewBag . Hora = DateTime . Now . ToShortTimeString () ; 11 return View () ; 12 } 13 } 14 } www.k19.com.br 73
C AMADA DE A PRESENTAÇÃO 74 Código C# 5.4: TesteController.cs 4 Crie uma página para exibir a hora atual do servidor. Para isto, crie o arquivo TestaView- Bag.cshtml dentro de uma pasta chamada ViewsTeste. 1 @{ 2 ViewBag . Title = " TestaViewBag " ; 3 } 4 5 < h2 > Hora do servidor : @ViewBag . Hora </ h2 > Código HTML 5.1: TestaViewBag.cshtml Para testar, acesse: http://localhost:<PORTA_APP>/Teste/TestaViewBag. 5 Crie uma página para mostrar as informações de um objeto da classe Editora. Este objeto deverá ser acessado através da propriedade Model. Altere o controlador Teste e acrescente a action TestaModel. Crie um objeto da classe Editora e passe-o como referência para a View através da propriedade Model. 1 ... 2 public ActionResult TestaModel () 3 { 4 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 5 return View ( editora ) ; 6 } 7 ... Código C# 5.5: Nova ação do controlador Teste Crie o arquivo TestaModel.cshtml que mostrará as informações do objeto da classe Editora na pasta ViewsTeste do projeto K19. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " TestaModel " ; 5 } 6 7 <p > 8 Nome : @Model . Nome 9 < br / > 10 Email : @Model . Email 11 </ p > Código HTML 5.2: TestaModel.cshtml Para ver o resultado, acesse a url http://localhost:<PORTA_APP>/Teste/TestaModel. HTML Helpers A função das páginas CSHTML é gerar hipertexto XHTML para enviar aos navegadores dos usuá- rios. Os arquivos .cshtml misturam tags XHTML com scripts de servidor escritos em C# (ou outra 74 www.k19.com.br
75 C AMADA DE A PRESENTAÇÃO linguagem de programação suportada pelo .NET). Essa mistura pode prejudicar a legibilidade do código em alguns casos. Além disso, a manutenção da aplicação pode se tornar mais complexa. Considere a criação de um link utilizando diretamente as tags HTML. 1 <a href = " / Editora / Index / " > Lista de editoras </ a > Código CSHTML 5.17: Link para a lista de editoras Se o formato da url para acessar a ação Index do controlador Editora for alterado, o código acima deverá ser modificado. Veremos como o formato das urls pode ser configurado no Capítulo 7. Para facilitar a manutenção do código das páginas CSHTML, o ASP.NET MVC oferece os chama- dos HTML Helpers. A função de um HTML Helper é encapsular um código XHTML. Por exemplo, para adicionar um link, podemos usar o método ActionLink do objeto Html, ao invés de usar a tag <a> do HTML diretamente. No código abaixo, criamos um link para a ação responsável por listar as editoras. 1 @Html . ActionLink ( " Lista de editoras " , " Index " , " Editora " ) Código CSHTML 5.18: Criando um link para a listagem de editoras Agora, se o formato da url para acessar a ação Index do controlador Editora for alterado, o có- digo acima não precisa ser alterado. ActionLink Helper O helper ActionLink é utilizado para gerar os links das páginas de uma aplicação web. Esse helper pode ser utilizado de diversas maneiras. A forma mais simples de utilizá-lo é passar a ele dois parâmetros: o texto e a ação associados ao link desejado. Nesse caso, o link gerado pelo helper ActionLink estará associado ao controlador correspondente a página na qual o link foi inserido. 1 @Html . ActionLink ( " TEXTO PARA O USUÁRIO " , " ACTION " ) Código CSHTML 5.19: Criando um link com controlador implícito Podemos definir o controlador desejado explicitamente. Para isso, é necessário passar um ter- ceiro parâmetro para o método ActionLink. 1 @Html . ActionLink ( " TEXTO PARA O USUÁRIO " , " ACTION " , " CONTROLADOR " ) Código CSHTML 5.20: Criando um link com controlador explícito O helper ActionLink permite que parâmetros sejam adicionados nos links gerados. Para isso, é necessário passar um array como parâmetro. 1 @Html . ActionLink ( " TEXTO PARA O USUÁRIO " , " ACTION " , new { Inicio = 0 , Final = 10 }) Código CSHTML 5.21: Acrescenta parâmetros de url www.k19.com.br 75
C AMADA DE A PRESENTAÇÃO 76 BeginForm e EndForm Helpers No ASP.NET MVC, há um conjunto de HTML Helpers para facilitar a criação de formulários nas páginas de uma aplicação web. Para criar um formulário, utilizamos o helper BeginForm. Para terminar um formulário, utiliza- mos o helper EndForm. Veja o exemplo a seguir. 1 @ { Html . BeginForm () ;} 2 3 <! -- elementos do formulário -- > 4 5 @ { Html . EndForm () ;} Código CSHTML 5.22: Utilizando os helpers BeginForm e EndForm Também podemos utilizar o comando using para garantir que os formulários sejam fechados. Nesse caso, não é necessário adicionar o helper EndForm. Observe o formulário abaixo. 1 @using ( Html . BeginForm () ) { 2 3 <! -- elementos de formulário -- > 4 5 } Código CSHTML 5.23: Utilizando o comando using Por padrão, um formulário criado com o helper BeginForm enviará requisições à ação associada à url atual. Contudo, podemos definir explicitamente uma outra ação para receber essas requisições. Veja um exemplo no código abaixo. 1 @using ( Html . BeginForm ( " ACTION " , " CONTROLADOR " ) ) { 2 3 <! -- elementos de formulário -- > 4 5 } Código CSHTML 5.24: Definindo uma ação e um controlador explicitamente Por padrão, os formulários gerados pelo helper BeginForm fazem requisições do tipo POST. Nesse caso, as ações associadas a esses formulários devem ser anotadas com HttpPost para indicar o tipo de requisição HTTP esperado. 1 public class K19Controller : Controller 2 { 3 // POST : / K19 / Home 4 [ HttpPost ] 5 public ActionResult Home () 6 { 7 ... 8 return View () ; 9 } 10 } Código C# 5.6: Anotando uma ação com HttpPost 76 www.k19.com.br
77 C AMADA DE A PRESENTAÇÃO CheckBox Podemos adicionar um checkbox em um formulário através do helper CheckBox. 1 @Html . CheckBox ( " Aceito " , false ) Código CSHTML 5.25: Utilizando o helper CheckBox O código acima produz o seguinte trecho de código HTML: 1 < input id = " Aceito " name = " Aceito " type = " checkbox " value = " true " / > 2 < input name = " Aceito " type = " hidden " value = " false " / > Código HTML 5.3: HTML gerado pelo helper CheckBox Considere o formulário a seguir: 1 @using ( Html . BeginForm ( " Cadastra " , " Contrato " ) ) 2 { 3 @Html . CheckBox ( " Aceito " , false ) 4 < input type = " submit " value = " Cadastra Contrato " / > 5 } Código CSHTML 5.26: Formulário Agora, considere a ação associada ao formulário criado no código acima. 1 public class ContratoController : Controller 2 { 3 // POST : / Contrato / Cadastra 4 [ HttpPost ] 5 public ActionResult Cadastra ( Contrato contrato ) 6 { 7 ... 8 return View () ; 9 } 10 } Código C# 5.7: Ação O valor enviado através do checkbox inserido no formulário será armazenado na propriedade Aceito do contrato enviado como parâmetro para a ação Cadastra(). Dessa forma, a classe Contrato deve possuir uma propriedade booleana chamada Aceito. 1 public class Contrato 2 { 3 public Boolean Aceito { get ; set ; } 4 } Código C# 5.8: Contrato.cs TextBox Uma caixa de texto pode ser adicionada através do helper TextBox. 1 @Html . TextBox ( " Nome " , " Digite o seu nome " ) www.k19.com.br 77
C AMADA DE A PRESENTAÇÃO 78 Código CSHTML 5.27: Utilizando o helper TextBox O código acima produz o seguinte trecho de código HTML: 1 < input id = " Nome " name = " Nome " type = " text " value = " Digite o seu nome " / > Código HTML 5.4: HTML gerado pelo helper TextBox TextArea Uma caixa para textos maiores pode ser adicionada através do helper TextArea. 1 @Html . TextArea ( " Mensagem " , " Digite uma mensagem " ) Código CSHTML 5.28: Utilizando o helper TextArea O código acima produz o seguinte trecho de código HTML: 1 < textarea cols = " 20 " id = " Mensagem " name = " Mensagem " rows = " 2 " > Digite uma mensagem </ ← textarea > Código HTML 5.5: HTML gerado pelo helper TextArea RadioButton Um botão do tipo radio pode ser adicionado através do helper RadioButton. 1 @Html . RadioButton ( " CidadeNatal " , " São Paulo " , true ) 2 @Html . RadioButton ( " CidadeNatal " , " Natal " , false ) 3 @Html . RadioButton ( " CidadeNatal " , " Piracicaba " , false ) Código CSHTML 5.29: Utilizando o helper RadioButton O código acima produz o seguinte trecho de código HTML: 1 < input checked = " checked " id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " São ← Paulo " / > 2 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Natal " / > 3 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Piracicaba " / > Código HTML 5.6: HTML gerado pelo helper RadioButton Hidden Um campo escondido pode ser adicionado através do helper Hidden. 1 @Html . Hidden ( " Id " , 1) Código CSHTML 5.30: Utilizando o helper Hidden 78 www.k19.com.br
79 C AMADA DE A PRESENTAÇÃO O código acima produz o seguinte trecho de código HTML: 1 < input id = " Id " name = " Id " type = " hidden " value = " 1 " / > Código HTML 5.7: HTML gerado pelo helper Hidden Password Uma caixa para senha pode ser adicionada através do helper Password. 1 @Html . Password ( " Password " , " senha " ) Código CSHTML 5.31: Utilizando o helper Password O código acima produz o seguinte trecho de código HTML: 1 < input id = " Password " name = " Password " type = " password " value = " senha " / > Código HTML 5.8: HTML gerado pelo helper Password Mais Sobre Os dados preenchidos nos elementos de um formulário são recuperados da ViewBag. Considere o código abaixo: 1 @Html . TextBox ( " Nome " ) Código CSHTML 5.32: Utilizando o helper TextBox Caso exista uma chave chamada Nome na ViewBag, então o valor associado a essa chave será o valor inicial da caixa de texto. Podemos alterar esse comportamento, passando um segundo parâmetro para o helper TextBox, que será o valor inicial da caixa de texto. Observe o código a seguir. 1 @Html . TextBox ( " Nome " , " NomeInicial " ) Código CSHTML 5.33: Utilizando o helper TextBox Strongly Typed Views Se os HTML Helpers forem utilizados de maneira análoga à mostrada anteriormente, a probabi- lidade de ocorrer um erro de digitação é alta. A forma que os HTML Helpers foram aplicados não permite que o compilador verifique a existência das propriedades associadas aos elementos dos for- mulários. Por exemplo, considere o código a seguir. 1 @Html . CheckBox ( " Aceito " , false ) Código CSHTML 5.34: Utilizando o helper CheckBox www.k19.com.br 79
C AMADA DE A PRESENTAÇÃO 80 O código anterior supõe que o objeto recebido como parâmetro pela ação associada ao formulá- rio onde o checkbox foi inserido possua uma propriedade chamada Aceito. Essa propriedade pode não existir. Contudo, nenhum erro de compilação seria gerado nesse caso. Para evitar esse tipo de problema, podemos utilizar a seguinte sintaxe em telas fortemente tipa- das: 1 @model K19 . Models . Contrato 2 ... 3 @Html . CheckBoxFor ( c = > c . Aceito ) 4 ... Código CSHTML 5.35: Utilizando o helper CheckBoxFor Com essa sintaxe, o compilador tem condições de verificar a existência das propriedades. Sendo assim, ele produzirá um erro de compilação se uma propriedade inexistente for utilizada. TextBoxFor 1 @model K19 . Models . Empresa 2 ... 3 @Html . TextBoxFor ( x = > x . Nome ) 4 ... Código CSHTML 5.36: Utilizando o helper TextBoxFor O código acima produz o seguinte trecho de código HTML: 1 < input id = " Nome " name = " Nome " type = " text " value = " K19 Treinamentos " / > Código HTML 5.9: HTML gerado pelo helper TextBoxFor TextAreaFor 1 @model K19 . Models . Empresa 2 ... 3 @Html . TextAreaFor ( x = > x . Descricao ) 4 ... Código CSHTML 5.37: Utilizando o helper TextAreaFor O código acima produz o seguinte trecho de código HTML: 1 < textarea cols = " 20 " id = " Descricao " name = " Descricao " rows = " 2 " > 2 K19 Treinamentos em Java e . NET 3 </ textarea > Código HTML 5.10: HTML gerado pelo helper TextAreaFor RadioButtonFor 80 www.k19.com.br
81 C AMADA DE A PRESENTAÇÃO 1 @model K19 . Models . Pessoa 2 ... 3 @Html . RadioButtonFor ( x = > x . CidadeNatal , " São Paulo " ) 4 @Html . RadioButtonFor ( x = > x . CidadeNatal , " Natal " ) 5 @Html . RadioButtonFor ( x = > x . CidadeNatal , " Piracicaba " ) 6 ... Código CSHTML 5.38: Utilizando o helper RadioButtonFor O código acima produz o seguinte trecho de código HTML: 1 < input checked = " checked " id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " São ← Paulo " / > 2 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Natal " / > 3 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Piracicaba " / > Código HTML 5.11: HTML gerado pelo helper RadioButtonFor HiddenFor 1 @model K19 . Models . Empresa 2 ... 3 @Html . HiddenFor ( e = > e . Id ) 4 ... Código CSHTML 5.39: Utilizando o helper HiddenFor O código acima produz o seguinte trecho de código HTML: 1 < input id = " Id " name = " Id " type = " hidden " value = " 1 " / > Código HTML 5.12: HTML gerado pelo helper HiddenFor PasswordFor 1 @model K19 . Models . Usuario 2 ... 3 @Html . PasswordFor ( x = > x . Senha ) 4 ... Código CSHTML 5.40: Utilizando o helper PasswordFor O código acima produz o seguinte trecho de código HTML: 1 < input id = " Senha " name = " Senha " type = " password " / > Código HTML 5.13: HTML gerado pelo helper PasswordFor Mais Sobre Os dados preenchidos nos elementos de um formulário de uma tela fortemente tipada são recuperados da propriedade Model. Considere o código abaixo: www.k19.com.br 81
C AMADA DE A PRESENTAÇÃO 82 1 @model K19 . Models . Pessoa 2 ... 3 @Html . TextBoxFor ( x = > x . Nome ) 4 ... Código CSHTML 5.41: Utilizando o helper TextBoxFor Caso exista a propriedade Model.Nome, o valor inicial dessa caixa de texto será o valor dessa propriedade. DropDownList Helper Considere uma aplicação para cadastrar livros de uma biblioteca. Quando um livro é cadastro, o usuário podem escolher a editora desse livro. A editora pode ser selecionada através de um drop down list. Para criar um drop down list, podemos utilizar o HTML Helper DropDownList.cshtml O primeiro passo para utilizar o helper DropDownList é criar uma SelectList na camada de controle com as opções que o usuário poderá selecionar. 1 public class LivroController : Controller 2 { 3 public ActionResult Create () 4 { 5 List < Editora > editoras = editoraRepository . BuscaTodas () ; 6 ViewBag . Editoras = new SelectList ( editoras , " Id " ," Nome " ) ; 7 return View () ; 8 } 9 } Código C# 5.9: LivroController.cs O segundo passo é adicionar o helper DropDownList na camada de apresentação. 1 @Html . DropDownList ( " Editoras " ) Código CSHTML 5.42: Utilizando o helper DropDownList EditorFor Considere a seguinte entidade. 1 public class Editora 2 { 3 public int Id { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public bool IsAtivo { get ; set ; } 7 public string Descricao { get ; set ; } 8 public virtual ICollection < Livro > Livros { get ; set ; } 9 } Código C# 5.10: Editora.cs 82 www.k19.com.br
83 C AMADA DE A PRESENTAÇÃO Para editar os dados de uma editora, temos uma página conforme o exemplo abaixo: 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Edição de Editora " ; 5 } 6 7 < h2 > Edição de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . HiddenFor ( model = > model . Id ) 11 12 @Html . LabelFor ( model = > model . Nome ) 13 @Html . TextBoxFor ( model = > model . Nome ) 14 15 < br / > 16 17 @Html . LabelFor ( model = > model . Email ) 18 @Html . TextBoxFor ( model = > model . Email ) 19 20 < br / > 21 22 @Html . LabelFor ( model = > model . Descricao ) 23 @Html . TextAreaFor ( model = > model . Descricao ) 24 25 < br / > 26 27 @Html . LabelFor ( model = > model . IsAtivo ) 28 @Html . CheckBoxFor ( model = > model . IsAtivo ) 29 30 < input type = " submit " value = " Salvar " / > 31 } Código CSHTML 5.43: Edit.cshtml Para cada propriedade da entidade Editora, utilizamos um helper para gerar o código HTML necessário para a entrada de dados. Por exemplo, no caso das propriedades Nome e Email, utilizamos o Helper TextBox. Para a propriedade booleana IsAtivo, utilizamos o helper CheckBox. Observe que a escolha do helper depende basicamente do tipo da propriedade associada a ele. Podemos deixar essa escolha a cargo do helper EditorFor. Para propriedades de tipo booleano, esse helper utilizará o CheckBox. Para propriedades do tipo string, esse helper utilizará o TextBox. Observe a utilização do helper EditorFor no código abaixo. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Edição de Editora " ; 5 } 6 7 < h2 > Edição de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . HiddenFor ( model = > model . Id ) 11 12 @Html . LabelFor ( model = > model . Nome ) 13 @Html . EditorFor ( model = > model . Nome ) 14 15 < br / > 16 17 @Html . LabelFor ( model = > model . Email ) 18 @Html . EditorFor ( model = > model . Email ) 19 20 < br / > 21 www.k19.com.br 83
C AMADA DE A PRESENTAÇÃO 84 22 @Html . LabelFor ( model = > model . Descricao ) 23 @Html . EditorFor ( model = > model . Descricao ) 24 25 < br / > 26 27 @Html . LabelFor ( model = > model . IsAtivo ) 28 @Html . EditorFor ( model = > model . IsAtivo ) 29 30 < input type = " submit " value = " Salvar " / > 31 } Código CSHTML 5.44: Edit.cshtml Para personalizar o funcionamento do helper EditorFor, podemos utilizar algumas anotações na entidade Editora. Por exemplo, seria mais apropriado utilizar um TextAreaFor para editar a descrição de uma editora ao invés de um TextBoxFor. Contudo, como dito anteriormente, o helper EditorFor utilizará um TextBoxFor para a propriedade Descricao. Nesse caso, podemos aplicar a anotação MultilineText na propriedade Descricao. 1 public class Editora 2 { 3 public int Id { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public bool IsAtivo { get ; set ; } 7 [ MultilineText ] 8 public string Descricao { get ; set ; } 9 public virtual ICollection < Livro > Livros { get ; set ; } 10 } Código C# 5.11: Editora.cs Além da anotação MultilineText, o ASP.NET MVC define as seguintes anotações para persona- lizar o helper EditorFor. Anotação Descrição HiddenInput Utiliza o Helper Hidden Text Utiliza o Helper Text String Utiliza o Helper TextBox Password Utiliza o Helper Password MultilineText Utiliza o Helper TextArea Boolean Utiliza o Helper CheckBox ou DropDownList para nullable boolean Decimal Utiliza o Helper TextBox e formata para duas casas decimais Object Percorre as propriedades do objeto e define o Helper adequado para cada uma Collection Percorre através do IEnumerable e define o Helper para cada elemento EditorForModel Também temos o helper EditorForModel para construir um formulário completo com base nas propriedades de uma entidade. Esse helper seguirá a mesma abordagem do EditorFor. Ou seja, para cada propriedade do Model, ele utilizará o helper apropriado. 1 @model K19 . Models . Editora 2 3 @{ 84 www.k19.com.br
85 C AMADA DE A PRESENTAÇÃO 4 ViewBag . Title = " Edição de Editora " ; 5 } 6 7 < h2 > Edição de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . EditorForModel () 11 12 < input type = " submit " value = " Save " / > 13 } Código CSHTML 5.45: Edit.cshtml Exercícios de Fixação 6 No projeto K19, defina uma página com um formulário de cadastro de editoras. Os campos nome e email deste formulário devem vir preenchidos com os valores dos atributos de um objeto da classe Editora. O nome e email deverão ser acessados através da propriedade ViewBag. Acrescente a ação TestaFormularioHtml no controlador Teste. 1 public ActionResult TestaFormularioHtml () 2 { 3 Editora e = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 ViewBag . Nome = e . Nome ; 5 ViewBag . Email = e . Email ; 6 return View () ; 7 } Código C# 5.12: Action TestaFormularioHtml do TesteController Crie a página TestaFormularioHtml.cshtml dentro da pasta Teste que fica na pasta Views. 1 @{ 2 ViewBag . Title = " TestaFormularioHtml " ; 3 } 4 < h2 > Cadastro de Editora </ h2 > 5 < form > 6 < label for = " Nome " > Nome : </ label > 7 < input id = " Nome " name = " Nome " value = " @ViewBag . Nome " / > 8 9 < label for = " Email " > Email : </ label > 10 < input id = " Email " name = " Email " value = " @ViewBag . Email " / > 11 12 < input type = " submit " value = " Enviar " / > 13 </ form > Código CSHTML 5.46: TestaFormularioHtml.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml. 7 Crie um formulário de cadastro de editoras conforme o exercício anterior, mas utilize Html Helpers. Altere o controlador Teste e acrescente a ação TestaHelpers. 1 public ActionResult TestaHelpers () 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View () ; 5 } www.k19.com.br 85
C AMADA DE A PRESENTAÇÃO 86 Código C# 5.13: Ação TestaHelpers do TesteController Crie a página TestaHelpers.cshtml dentro da pasta Teste que fica na pasta Views. 1 @{ 2 @ViewBag . Title = " TestaHelpers " ; 3 } 4 5 < h2 > Cadastro de Editora usando Helpers </ h2 > 6 @using ( Html . BeginForm () ) 7 { 8 @Html . Label ( " Nome " ) 9 @Html . TextBox ( " Nome " ) 10 @Html . Label ( " Email " ) 11 @Html . TextBox ( " Email " ) 12 < input type = " submit " value = " Enviar " / > 13 } Código CSHTML 5.47: TestaHelpers.cshtml Para testar, acesse acesse a url http://localhost:<PORTA_APP>/Teste/TestaHelpers. 8 Crie um novo formulário de cadastro de editoras conforme o exercício anterior, mas utilize HTML Helpers Strongly Typed. Altere o controlador Teste e acrescente a ação TestaHelpersTipados. 1 public ActionResult TestaHelpersTipados () 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View ( editora ) ; 5 } Código C# 5.14: Action TestaHelpersTipados do TesteController Crie a página TestaHelpersTipados.cshtml dentro da pasta Teste que fica na pasta Views. 1 @model K19 . Models . Editora 2 3 @{ 4 @ViewBag . Title = " TestaHelpersTipados " ; 5 } 6 7 < h2 > Cadastro de editoras usando HTML Helpers Strongly Typed </ h2 > 8 @using ( Html . BeginForm () ) 9 { 10 @Html . LabelFor ( x = > x . Nome ) 11 @Html . TextBoxFor ( x = > x . Nome ) 12 @Html . LabelFor ( x = > x . Email ) 13 @Html . TextBoxFor ( x = > x . Email ) 14 < input type = " submit " value = " Enviar " / > 15 } Código CSHTML 5.48: TestaHelpersTipados.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Teste/TestaHelpersTipados. 9 Conforme os exercícios anteriores, crie um formulário de cadastro de editoras utilizando o HTML Helper EditorFor. Altere o controlador Teste e acrescente a seguinte ação. 1 public ActionResult TestaEditorFor () 2 { 86 www.k19.com.br
87 C AMADA DE A PRESENTAÇÃO 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View ( editora ) ; 5 } Código C# 5.15: Ação TestaEditorFor do TesteController Crie a página TestaEditorFor.cshtml dentro da pasta Teste que fica na pasta Views. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " TestaEditorFor " ; 5 } 6 7 < h2 > Cadastro de editoras usando EditorFor </ h2 > 8 @using ( Html . BeginForm () ) 9 { 10 @Html . LabelFor ( x = > x . Nome ) 11 @Html . EditorFor ( x = > x . Nome ) 12 @Html . LabelFor ( x = > x . Email ) 13 @Html . EditorFor ( x = > x . Email ) 14 < input type = " submit " value = " Enviar " / > 15 } Código HTML 5.14: TestaEditorFor.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Teste/TestaEditorFor. Layouts É comum que as páginas de uma aplicação web possuam conteúdo em comum (por exemplo, um cabeçalho ou um rodapé). O conteúdo em comum pode ser replicado em todas as páginas através do CTRL+C e CTRL+V. Porém, essa não é uma boa abordagem, pois quando alguma alteração precisa ser realizada, todos os arquivos devem ser modificados. Também é comum que as páginas de uma aplicação web possuam um certo padrão visual. Daí surge o conceito de Layouts. Conteúdo comum Tudo que é comum a todas as páginas de um determinado grupo pode ser definido em um Layout. Dessa forma, qualquer alteração é facilmente realizada modificando-se apenas um arquivo. Por exemplo, suponha que toda página de uma aplicação web deva ter o mesmo título e a mesma formatação. Podemos criar um Layout com o título desejado e com a referência ao arquivo CSS que define a formatação padrão. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < title > @ViewBag . Title </ title > 5 @Styles . Render ( " ~/ Content / Site . css " ) 6 @Scripts . Render ( " ~/ Scripts / jquery -1.5.1. min . js " ) 7 </ head > 8 9 < body > 10 < div id = " header " > 11 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 12 @Html . ActionLink ( " Livros " , " Index " ," Livro " ) www.k19.com.br 87
C AMADA DE A PRESENTAÇÃO 88 13 </ div > 14 @RenderBody () 15 </ body > 16 </ html > Código CSHTML 5.49: K19Layout.cshtml No layout definido no código acima, utilizamos o método Render() das propriedades Styles e Scripts para adicionar conteúdo CSS e JavaScript. Na tag <title>, acrescentamos @ViewBag.Title, para que cada página possa definir o seu próprio título. Por fim, o método RenderBody() indica onde o conteúdo definido em cada página será adicionado. É recomendado que os arquivos que definam layouts sejam colocados na pasta ViewsShare. Mais Sobre Suponha que devemos adicionar diversos arquivos CSS à uma página de uma aplicação ASP.NET MVC. Se esses arquivos forem adicionados um a um da maneira tradicional (com a tag <link>), o número de requisições que os navegadores terão de realizar para carre- gar essa página será alto, afetando o desempenho. Para contornar esse problema, poderíamos agrupar o código CSS de todos os arquivos em apenas um. Contudo, essa abordagem dificultaria a manutenção do código CSS. Dessa forma, o ASP.NET MVC 4 possui um mecanismo capaz de agrupar o conteúdo CSS de diversos arquivos dinamicamente. Para usar esse mecanismo, basta invocar o método Render() da propriedade Styles. 1 @Styles . Render ( " ~/ Content / Arquivo1 . css " , " ~/ Content / Arquivo2 . css " , " ~/ Content / Arquivo3 ← . css " ) Código CSHTML 5.50: Utilizando o método Render para adicionar conteúdo CSS Analogamente, a propriedade Scripts possui o método Render(), que também tem por obje- tivo agrupar o código JavaScript de diversos arquivos. Com o layout definido, o próximo passo é indicar quais páginas utilizarão esse layout. Por exem- plo, podemos atualizar a página de edição de editoras para utilizar K19Layout.cshtml como layout. 1 @model K19 . Models . Editora 2 3 @{ 4 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 5 6 // Define o título específico desta página 7 ViewBag . Title = " Edição de Editora " ; 8 } 9 10 < h2 > Edição de Editora </ h2 > 11 12 @using ( Html . BeginForm () ) { 13 @Html . EditorForModel () 14 < input type = " submit " value = " Salvar " / > 15 } Código CSHTML 5.51: Edit.cshtml Quando a página de edição de editoras é requisitada, o arquivo Edit.cshtml é processado antes do arquivo K19Layout.cshtml, o que permite definir o valor de ViewBag.Title no arquivo Edit.cshtml 88 www.k19.com.br
89 C AMADA DE A PRESENTAÇÃO e utilizá-lo no layout. Para definir o layout de Edit.cshtml, foi necessário armazenar o caminho completo do arquivo que define o layout na propriedade Layout. Este procedimento não é muito prático, pois em cada página que deseja utilizar esse layout é necessário definir esta propriedade. A partir da terceira versão do ASP.NET MVC, temos uma nova funcionalidade que permite definir um layout padrão para todas as páginas, não havendo necessidade de definir a propriedade Layout em cada página. Para isso, basta acrescentarmos o arquivo _ViewStart.cshtml à pasta View: O _ViewStart.cshtml permite definirmos um código que será executado antes de cada página ser renderizada. Nesse arquivo podemos definir, por exemplo, a propriedade Layout: 1 @{ 2 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 3 } Código CSHTML 5.52: _ViewStart.cshtml Como este código é processado antes dos arquivos específicos de cada página, não há mais ne- cessidade de definir a propriedade Layout em cada arquivo específico. Lacunas Também podemos criar seções em um layout para serem preenchidas com conteúdos específicos definidos nas páginas. Considere o seguinte layout. 1 <! DOCTYPE html > www.k19.com.br 89
C AMADA DE A PRESENTAÇÃO 90 2 < html > 3 < head > 4 < title > @ViewBag . Title </ title > 5 @Styles . Render ( " ~/ Content / Site . css " ) 6 @Scripts . Render ( " ~/ Scripts / jquery -1.5.1. min . js " ) 7 </ head > 8 9 < body > 10 < div id = " header " > 11 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 12 @Html . ActionLink ( " Livros " , " Index " ," Livro " ) 13 </ div > 14 < div id = " sidebar " > @RenderSection ( " Sidebar " , false ) </ div > 15 < div id = " content " > @RenderBody () </ div > 16 < div id = " footer " > K19 Treinamentos </ div > 17 </ body > 18 </ html > Código CSHTML 5.53: K19Layout.cshtml No código acima, utilizamos o método @RenderSection() para criar uma seção no layout. O primeiro parâmetro (“Sidebar”) é o nome da seção e o segundo é um booleano que indica se o pre- enchimento dessa seção é obrigatório ou não. Para definir o conteúdo de uma seção, devemos utilizar o código @section. Observe o código de uma página que utiliza o layout criado anteriormente e define a seção Sidebar. 1 @model K19 . Models . Editora 2 3 @{ 4 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 5 6 // Define o título específico desta página 7 ViewBag . Title = " Edição de Editora " ; 8 } 9 10 < h2 > Edição de Editora </ h2 > 11 12 @using ( Html . BeginForm () ) { 13 @Html . EditorForModel () 14 < input type = " submit " value = " Salvar " / > 15 } 16 17 @section Sidebar { 18 <p > Sidebar do cadastro de Edição de Editora </ p > 19 } Código CSHTML 5.54: Edit.cshtml Exercícios de Fixação 10 Crie um arquivo chamado K19Layout.cshtml dentro da pasta Shared que fica na pasta Views do projeto K19. Este arquivo servirá de modelo para as páginas da nossa aplicação. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 90 www.k19.com.br
91 C AMADA DE A PRESENTAÇÃO 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 14 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 15 16 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 17 18 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 19 20 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 21 </ div > 22 @RenderBody () 23 24 @Scripts . Render ( " ~/ bundles / jquery " ) 25 @RenderSection ( " scripts " , required : false ) 26 </ body > 27 </ html > Código CSHTML 5.55: K19Layout.cshtml 11 Altere a página TestaFormularioHtml.cshtml para utilizar a página de layout definida no exer- cício anterior. 1 @{ 2 ViewBag . Title = " TestaFormularioHtml " ; 3 } 4 5 @{ 6 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 7 } 8 9 < h2 > Cadastro de Editora </ h2 > 10 < form > 11 < label for = " Nome " > Nome : </ label > 12 < input id = " Nome " name = " Nome " value = " @ViewBag . Nome " / > 13 14 < label for = " Email " > Email : </ label > 15 < input id = " Email " name = " Email " value = " @ViewBag . Email " / > 16 17 < input type = " submit " value = " Enviar " / > 18 </ form > Código HTML 5.15: TestaFormularioHtml.cshtml Teste acessando o endereço: http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml. 12 Defina a página K19Layout.cshtml como layout padrão para todas as telas. Altere o arquivo _ViewStart.cshtml da pasta Views. 1 @{ 2 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 3 } Código CSHTML 5.56: _ViewStart.cshtml Teste acessando http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml e navegando pelos links. 13 Defina seções no layout K19Layout.cshtml. Primeiramente, acrescente ao arquivo Site.css da pasta Content o trecho de código a seguir: www.k19.com.br 91
C AMADA DE A PRESENTAÇÃO 92 1 # sidebar 2 { 3 float : right ; 4 margin :10 px ; 5 padding :10 px ; 6 border : dotted 5 px red ; 7 width :180 px ; 8 } 9 10 # footer 11 { 12 text - align : center ; 13 clear : both ; 14 } 15 16 # content 17 { 18 float : left ; 19 margin :10 px ; 20 } 21 ... Código CSS 5.1: Site.css Finalmente, altere o arquivo K19Layout.cshtml da pasta ViewsShared. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 14 15 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 16 17 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 18 19 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 20 </ div > 21 < div id = " sidebar " > SideBar Padrão </ div > 22 < div id = " content " > @RenderBody () </ div > 23 < div id = " footer " > K19 Treinamentos </ div > 24 25 @Scripts . Render ( " ~/ bundles / jquery " ) 26 @RenderSection ( " scripts " , required : false ) 27 </ body > 28 </ html > Código CSHTML 5.57: K19Layout.cshtml Teste acessando http://localhost:<PORTA_APP>/Editora e navegando pelos links.. 14 Acrescente uma seção ao layout K19Layout.cshtml. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 92 www.k19.com.br
93 C AMADA DE A PRESENTAÇÃO 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 14 15 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 16 17 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 18 19 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 20 </ div > 21 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 22 < div id = " content " > @RenderBody () </ div > 23 < div id = " footer " > K19 Treinamentos </ div > 24 25 @Scripts . Render ( " ~/ bundles / jquery " ) 26 @RenderSection ( " scripts " , required : false ) 27 </ body > 28 </ html > Código CSHTML 5.58: K19Layout.cshtml 15 Defina na página TestaFormularioHtml.cshtml a seção “Sidebar”. 1 @{ 2 ViewBag . Title = " TestaFormularioHtml " ; 3 } 4 5 @{ 6 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 7 } 8 9 < h2 > Cadastro de Editora </ h2 > 10 < form > 11 < label for = " Nome " > Nome : </ label > 12 < input id = " Nome " name = " Nome " value = " @ViewBag . Nome " / > 13 14 < label for = " Email " > Email : </ label > 15 < input id = " Email " name = " Email " value = " @ViewBag . Email " / > 16 17 < input type = " submit " value = " Enviar " / > 18 </ form > 19 20 @section Sidebar { 21 <p > Sidebar do TestaFormularioHtml </ p > 22 } Código CSHTML 5.59: TestaFormularioHtml.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml e navegue pelos links.. 16 Acrescente uma seção padrão que será utilizada nas páginas que não definiram a seção “Side- bar”. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) www.k19.com.br 93
C AMADA DE A PRESENTAÇÃO 94 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 14 15 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 16 17 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 18 19 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 20 </ div > 21 @if ( IsSectionDefined ( " Sidebar " ) ) 22 { 23 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 24 } 25 else 26 { 27 < div id = " sidebar " > Sidebar padrão </ div > 28 } 29 < div id = " content " > @RenderBody () </ div > 30 < div id = " footer " > K19 Treinamentos </ div > 31 32 @Scripts . Render ( " ~/ bundles / jquery " ) 33 @RenderSection ( " scripts " , required : false ) 34 </ body > 35 </ html > Código CSHTML 5.60: K19Layout.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Editora e navegue pelos links.. Partial views Quanto mais elaborada é uma página de uma aplicação web, mais extenso é o seu código. Códi- gos muito extensos prejudicam a legibilidade e a manutenção da aplicação. Para organizar melhor o código, podemos dividir o conteúdo de uma página web em vários arquivos. Suponha que desejamos dividir o conteúdo de uma página em duas partes. Devemos criar um arquivo para cada parte. 1 < h1 > Parte 1 </ h > 2 <p > Conteúdo da parte1 </ p > Código CSHTML 5.61: _Parte1.cshtml 1 < h1 > Parte 2 </ h > 2 <p > Conteúdo da parte2 </ p > Código CSHTML 5.62: _Parte2.cshtml Mais Sobre Por convenção, o nome dos arquivos que definem as telas parciais de uma aplicação ASP.NET MVC devem iniciar com o caractere “_”. 94 www.k19.com.br
95 C AMADA DE A PRESENTAÇÃO Por fim, devemos criar um arquivo principal para agrupar essas partes. Utilizaremos o método Partial() para inserir o conteúdo dos arquivos secundários no arquivo principal. 1 < html > 2 < head > 3 < title > Exemplo de partial </ title > 4 </ head > 5 6 < body > 7 Html . Partial ( " _Parte1 " ) 8 Html . Partial ( " _Parte2 " ) 9 </ body > 10 </ html > Código CSHTML 5.63: Principal.cshtml O método Partial() procura os arquivos _Parte1.cshtml e _Parte2.cshtml no mesmo dire- tório do arquivo principal. Caso ele não encontre, ele continua procurando esses arquivos na pasta ViewsShared. Essa lógica também é aplicada no método View que utilizamos na camada de con- trole. O recurso de páginas parciais permite a criação de conteúdo reutilizável de forma mais clara e concisa. Informações podem ser compartilhadas entre as páginas principais e as páginas parciais através da propriedade ViewBag. Por exemplo, podemos definir duas páginas principais, uma para criar e outra para editar editoras. Nessas duas páginas podemos acrescentar uma página parcial com o formulário que pode ser utilizado para cadastrar novas editoras ou editar editoras existentes. Veja abaixo o exemplo da página parcial que contém o formulário de cadastro ou edição de edi- toras. 1 <! -- ~/ Views / Editora / _Form . cshtml -- > 2 @model K19 . Models . Editora 3 4 @Scripts . Render ( 5 " ~/ Scripts / jquery -1.5.1. min . js " , 6 " ~/ Scripts / jquery . validate . min . js " , 7 " ~/ Scripts / jquery . validate . unobtrusive . min . js " ) 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 < fieldset > 12 < legend > Editora </ legend > 13 @if ( Model != null ) 14 { 15 @Html . EditorFor ( model = > model . Id ) 16 } 17 18 < div class = " editor - label " > 19 @Html . LabelFor ( model = > model . Nome ) 20 </ div > 21 < div class = " editor - field " > 22 @Html . EditorFor ( model = > model . Nome ) 23 </ div > 24 25 < div class = " editor - label " > 26 @Html . LabelFor ( model = > model . Email ) 27 </ div > 28 < div class = " editor - field " > 29 @Html . EditorFor ( model = > model . Email ) 30 </ div > 31 32 < div class = " editor - label " > 33 @Html . LabelFor ( model = > model . IsAtivo ) www.k19.com.br 95
C AMADA DE A PRESENTAÇÃO 96 34 </ div > 35 < div class = " editor - field " > 36 @Html . EditorFor ( model = > model . IsAtivo ) 37 </ div > 38 39 <p > 40 < input type = " submit " value = " Save " / > 41 </ p > 42 </ fieldset > 43 } Código CSHTML 5.64: _Form.cshtml Veja abaixo o exemplo das páginas principais que utilizam a página parcial definida no código acima. 1 <! -- ~/ Views / Editora / Create . cshtml -- > 2 @model K19 . Models . Editora 3 4 @{ 5 ViewBag . Title = " Cadastro de Editora " ; 6 } 7 8 < h2 > Cadastro de Editora </ h2 > 9 10 @Html . Partial ( " _Form " ) Código CSHTML 5.65: Create.cshtml 1 <! -- ~/ Views / Editora / Edit . cshtml -- > 2 @model LivrariaVirtual . Models . Editora 3 4 @{ 5 ViewBag . Title = " Edição de Editora " ; 6 } 7 8 < h2 > Edição de Editora </ h2 > 9 10 @Html . Partial ( " _Form " ) Código CSHTML 5.66: Edit.cshtml Exercícios de Fixação 17 Crie uma página principal composta por outras páginas parciais. Para isto, altere o controlador Teste do projeto K19 e acrescente a ação chamada TestaPartial. 1 ... 2 public ActionResult TestaPartial () 3 { 4 return View () ; 5 } 6 ... Código C# 5.16: Nova ação TestaPartial do controlador Teste 18 Defina duas páginas parciais. Crie os arquivos _Parcial1.cshtml e _Parcial2.cshtml na pasta Teste que fica na pasta Views do projeto K19. 96 www.k19.com.br
97 C AMADA DE A PRESENTAÇÃO 1 < h2 > Parcial1 </ h2 > Código CSHTML 5.67: _Parcial1.cshtml 1 < h2 > Parcial2 </ h2 > Código CSHTML 5.68: _Parcial2.cshtml 19 Crie uma página principal chamada TestaPartial.cshtml na pasta Teste que fica na pasta Views do projeto K19 e adicione as duas páginas parciais criadas anteriormente. 1 @{ 2 ViewBag . Title = " TestaPartial " ; 3 } 4 5 < h2 > Testando Partial </ h2 > 6 7 @Html . Partial ( " _Parcial1 " ) 8 @Html . Partial ( " _Parcial2 " ) Código CSHTML 5.69: TestaPartial.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Teste/TestaPartial www.k19.com.br 97
C AMADA DE A PRESENTAÇÃO 98 98 www.k19.com.br
CAPÍTULO C AMADA DE C ONTROLE 6 No ASP.NET MVC as urls são mapeadas para métodos (ações) em classes que definem os chama- das controladores. As requisições enviadas pelos navegadores são processadas pelos controlado- res. O processamento realizado por um controlador para tratar uma requisição consiste basicamente em: • Recuperar os dados enviados pelo usuário através de formulários. • Interagir com a camada de modelo. • Acionar a camada de apresentação para construir a página HTML que deve ser enviada para o usuário como resposta à sua requisição. Para que uma classe seja considerada um controlador, ela deve seguir algumas regras. • O nome da classe deve ter o sufixo “Controller”. • A classe deve implementar a interface System.Web.Mvc.IController ou herdar da classe Sys- tem.Web.Mvc.Controller. Raramente, você definirá uma classe para criar um controlador implementando a interface ICon- troller. Comumente as classes que definem um controlador derivam de Controller. Diversas propriedade e métodos são herdados da classe Controller. Essas propriedades facilitam o proces- samento das requisições e a utilização dos recursos do ASP.NET MVC. Actions Os controladores e as ações são elementos fundamentais de uma aplicação ASP.NET MVC. Um controlador pode conter diversas ações. As ações são utilizadas para processar as requisições realiza- das pelos navegadores. Para criar uma ação, é necessário definir um método public dentro da classe de um controlador. Os parâmetros desse método podem ser utilizados para receber os dados envia- dos pelos usuários através de formulários HTML. Esse método deve devolver um ActionResult que será utilizado pelo ASP.NET MVC para definir o que deve ser executado depois que a ação terminar. Quando um usuário faz uma requisição HTTP através de um navegador, o ASP.NET MVC verifica na tabela de rotas o controlador e a ação associados à url da requisição realizada. Essa tabela é definida no arquivo RouteConfig.cs e inicializada no arquivo Global.asax. Por padrão, quando criamos um projeto ASP.NET MVC no Visual Web Developer, uma rota com o formato {controller}/{action}/{id} é adicionada na tabela de rotas. Com essa rota, se uma www.k19.com.br 99
C AMADA DE C ONTROLE 100 requisição for realizada para a url http://www.k19.com.br/Editora/Listagem, o controlador defi- nido pela classe EditoraController e a ação implementada pelo método Listagem() dessa classe serão escolhidos para processar essa requisição. Se uma requisição for realizada para a url http://www.k19.com.br/Editora/Remove/1, o con- trolador definido pela classe EditoraController e a ação implementada pelo método Remove() dessa classe serão escolhidos para processar a requisição realizada. Além disso, o valor 1 será pas- sado como parâmetro para o método Remove(). Veremos mais sobre rotas no Capítulo 7. ActionResult Quando uma ação termina, o método correspondente deve devolver um ActionResult. O valor devolvido indica para o ASP .NET MVC o que deve ser executado depois da ação. Veja abaixo uma lista com alguns tipos específicos de ActionResult que podem ser utilizados. ViewResult: Devolve uma página da camada de apresentação. Considere o seguinte exemplo. 1 public class TesteController 2 { 3 public ActionResult Acao () 4 { 5 return View () ; 6 } 7 } Considerando uma aplicação ASP .NET MVC 4 em C# e Razor, o método View(), ao ser cha- mado sem parâmetros, executará o seguinte processo para determinar qual arquivo deve ser utilizado para construir a página de resposta. 1. Se o arquivo ViewsTesteAcao.cshtml existir, ele será utilizado. 2. Caso contrário, se o arquivo ViewsSharedAcao.cshtml existir, ele será utilizado. 3. Se nenhum desses arquivos existir, uma página de erro será devolvida. Por outro lado, podemos especificar o nome do arquivo que define a página de resposta. Veja o exemplo abaixo. 1 return View ( " NomeDaView " ) ; Além disso, podemos passar um objeto para a camada de apresentação. Esse objeto será utili- zado nas páginas fortemente tipadas como vimos no Capítulo 5. 1 return View ( editora ) ; Também é possível especificar o nome do arquivo que define a página de resposta e o objeto que deve ser transmitido para a camada de apresentação ao mesmo tempo. 1 return View ( " NomeDaView " , editora ) ; PartialViewResult: Devolve uma página parcial da camada de apresentação. Exemplos: 100 www.k19.com.br
101 C AMADA DE C ONTROLE 1 return PartialView () ; O método PartialView() utiliza a mesma abordagem do método View() para determinar o arquivo que deve ser utilizado para construir a página parcial de resposta. 1 return PartialView ( " NomeDaPartialView " , editora ) ; RedirectResult: Redireciona o navegador para uma URL específica. Exemplo: 1 return Redirect ( " http :// www . k19 . com . br " ) ; RedirectToAction: Redireciona para outra ação da camada de controle. Exemplo: 1 return RedirectToAction ( " OutraAction " , " OutroController " ) ; ContentResult: Devolve texto. Exemplo: 1 return Content ( " Texto " ," text plain " ) ; JsonResult: Devolve um objeto no formato JSON. Exemplo: 1 return Json ( editora ) ; JavaScriptResult: Devolve código Javascript. Exemplo: 1 return JavaScript ( " $ ( ’# divResultText ’) . html ( ’ JavaScript Passed ’) ; " ) ; FileResult: Devolve dados binários. Exemplo: 1 return File ( @ " c : relatorio . pdf " , " application pdf " ) ; EmptyResult: Devolve uma resposta vazia. Exemplo: 1 return new EmptyResult () ; Parâmetros Os parâmetros enviados pelos usuários através de formulários HTML podem ser recuperados por meio da propriedade Request. 1 string nome = Request [ " Nome " ]; Código C# 6.14: Recuperando o parâmetro Nome enviado em uma requisição HTTP Esses parâmetros também podem ser recuperados através dos parâmetros da ação responsável pelo processamento da requisição HTTP realizada. Para isso, basta definir um parâmetro C# para cada parâmetro HTTP com o mesmo nome. Veja o exemplo abaixo: www.k19.com.br 101
C AMADA DE C ONTROLE 102 1 < html > 2 < head > 3 < title > Cadastro de Editora </ title > 4 </ head > 5 < body > 6 < form action = " / Editoras / Salva " method = " post " > 7 Nome : < input type = " text " name = " nome " / > 8 Email : < input type = " text " name = " email " / > 9 < input type = " submit " / > 10 </ form > 11 </ body > 12 </ html > Código CSHTML 6.1: Cadastra.csthml No código acima, criamos um formulário HTML com os parâmetros nome e email. Esses parâ- metros serão recuperados na ação Salva definida no código abaixo. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( string nome , string email ) 4 { 5 Editora editora = new Editora { Nome = nome , Email = email }; 6 db . Editoras . Add ( editora ) ; 7 return View () ; 8 } 9 ... Código C# 6.15: EditoraController.cs O ASP.NET MVC também é capaz de montar objetos com os valores dos parâmetros HTTP envi- ados pelo usuário e passá-los como argumento para as ações dos controladores. 1 [ HttpPost ] 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 return View () ; 6 } Código C# 6.16: EditoraController.cs As propriedades dos objetos recebidos como argumentos nas ações dos controladores precisam ter os mesmos nomes dos parâmetros HTTP ignorando-se letras maiúsculas ou minúsculas. Exercícios de Fixação 1 Adicione no controlador Editora do projeto K19 uma ação para visualizar o formulário de ca- dastro de editoras. 1 ... 2 public ActionResult Cadastra () 3 { 4 return View () ; 5 } 6 ... Código C# 6.17: EditoraController.cs 102 www.k19.com.br
103 C AMADA DE C ONTROLE 2 Crie arquivo chamado Cadastra.cshtml na pasta ViewsEditora. Nesse arquivo implemente um formulário para cadastrar de editoras. 1 @{ 2 ViewBag . Title = " Cadastra " ; 3 } 4 < h2 > Cadastro de Editoras </ h2 > 5 6 < form action = " / Editora / Salva " > 7 Nome : < input name = " Nome " / > 8 Email : < input name = " Email " / > 9 < input type = " submit " value = " Enviar " / > 10 </ form > Código CSHTML 6.2: Cadastra.cshtml 3 Defina uma ação para salvar editoras no controlador Editora. Essa ação receberá os dados enviados pelo usuário e adicionará uma editora ao banco de dados. Para receber os dados, utilize a propriedade Request. 1 ... 2 public ActionResult Salva () 3 { 4 Editora editora = new Editora { Nome = Request [ " Nome " ] , Email = Request [ " Email " ] }; 5 db . Editoras . Add ( editora ) ; 6 db . SaveChanges () ; 7 return RedirectToAction ( " Index " ) ; 8 } 9 ... Código C# 6.18: EditoraController.cs Para testar, cadastre uma editora acessando a url http://localhost:<PORTA_APP>/Editora/ Cadastra 4 Altere ação criada anteriormente para receber os dados do usuário através do parâmetro do método. 1 ... 2 public ActionResult Salva ( string nome , string email ) 3 { 4 Editora editora = new Editora { Nome = nome , Email = email }; 5 db . Editoras . Add ( editora ) ; 6 db . SaveChanges () ; 7 return RedirectToAction ( " Index " ) ; 8 } 9 ... Código C# 6.19: EditoraController.cs Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra 5 Altere novamente a ação criada anteriormente. Agora, ela deve receber uma editora completa como parâmetro. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 db . SaveChanges () ; 6 return RedirectToAction ( " Index " ) ; 7 } www.k19.com.br 103
C AMADA DE C ONTROLE 104 8 ... Código C# 6.20: EditoraController.cs Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra TempData Ao efetuar um redirecionamento, uma nova requisição é realizada pelo navegador. Nesta nova requisição, não temos mais acesso aos dados e objetos da requisição anterior ao redirecionamento. Caso haja a necessidade de preservar dados ao longo do redirecionamento, podemos utilizar a pro- priedade TempData. Por exemplo, ao cadastrar uma editora, podemos efetuar um redirecionamento para a tela de listagem de editoras. Na propriedade TempData, podemos acrescentar uma mensagem indicando o eventual sucesso da operação. Veja o exemplo abaixo. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( Editora editora ) 4 { 5 db . Editoras . Add ( editora ) ; 6 db . SaveChanges () ; 7 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 ... Código C# 6.21: EditoraController.cs Na camada de apresentação, podemos recuperar os dados armazenados na propriedade TempData. Veja o código a seguir: 1 @if ( TempData [ " Mensagem " ] != null ) 2 { 3 <p > @TempData [ " Mensagem " ] </ p > 4 } Código CSHTML 6.3: Recuperando dados armazenados na propriedade TempData Exercícios de Fixação 6 Ao adicionar uma editora, o usuário é redirecionado para a ação de listagem de editoras. Mos- tre uma mensagem na página de listagem para indicar que a operação foi realizada com sucesso. Inicialmente, tente armazenar essa mensagem na ViewBag. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 db . SaveChanges () ; 6 ViewBag . Mensagem = " Editora cadastrada com sucesso ! " ; 7 return RedirectToAction ( " Index " ) ; 8 } 104 www.k19.com.br
105 C AMADA DE C ONTROLE 9 ... Código C# 6.22: EditoraController.cs 7 Altere o arquivo Index.cshtml da pasta ViewsEditora para mostrar a mensagem armazena na ViewBag. 1 @model IEnumerable < K19 . Models . Editora > 2 3 @{ 4 ViewBag . Title = " Index " ; 5 } 6 7 < h2 > Index </ h2 > 8 9 @if ( ViewBag . Mensagem != null ) 10 { 11 < h2 > Mensagem : @ViewBag . Mensagem </ h2 > 12 } 13 14 <p > 15 @Html . ActionLink ( " Create New " , " Create " ) 16 </ p > 17 < table > 18 < tr > 19 < th > 20 @Html . DisplayNameFor ( model = > model . Nome ) 21 </ th > 22 < th > 23 @Html . DisplayNameFor ( model = > model . Email ) 24 </ th > 25 < th > </ th > 26 </ tr > 27 28 @foreach ( var item in Model ) { 29 < tr > 30 < td > 31 @Html . DisplayFor ( modelItem = > item . Nome ) 32 </ td > 33 < td > 34 @Html . DisplayFor ( modelItem = > item . Email ) 35 </ td > 36 < td > 37 @Html . ActionLink ( " Edit " , " Edit " , new { id = item . Id }) | 38 @Html . ActionLink ( " Details " , " Details " , new { id = item . Id }) | 39 @Html . ActionLink ( " Delete " , " Delete " , new { id = item . Id }) 40 </ td > 41 </ tr > 42 } 43 </ table > Código CSHTML 6.4: Index.cshtml Teste a mensagem cadastrando uma editora através da url http://localhost:<PORTA_APP>/Editora/ Cadastra. Verifique que a mensagem de sucesso não aparecerá. 8 Armazene a mensagem de sucesso na propriedade TempData ao invés da ViewBag. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 db . SaveChanges () ; 6 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 7 return RedirectToAction ( " Index " ) ; 8 } www.k19.com.br 105
C AMADA DE C ONTROLE 106 9 ... Código C# 6.23: EditoraController.cs 9 Recupere a mensagem de sucesso da propriedade TempData ou invés da ViewBag. 1 2 @model IEnumerable < K19 . Models . Editora > 3 4 @{ 5 ViewBag . Title = " Index " ; 6 } 7 8 < h2 > Index </ h2 > 9 10 @if ( TempData [ " Mensagem " ] != null ) 11 { 12 < h2 > Mensagem : @TempData [ " Mensagem " ] </ h2 > 13 } 14 15 <p > 16 @Html . ActionLink ( " Create New " , " Create " ) 17 </ p > 18 < table > 19 < tr > 20 < th > 21 @Html . DisplayNameFor ( model = > model . Nome ) 22 </ th > 23 < th > 24 @Html . DisplayNameFor ( model = > model . Email ) 25 </ th > 26 < th > </ th > 27 </ tr > 28 29 @foreach ( var item in Model ) { 30 < tr > 31 < td > 32 @Html . DisplayFor ( modelItem = > item . Nome ) 33 </ td > 34 < td > 35 @Html . DisplayFor ( modelItem = > item . Email ) 36 </ td > 37 < td > 38 @Html . ActionLink ( " Edit " , " Edit " , new { id = item . Id }) | 39 @Html . ActionLink ( " Details " , " Details " , new { id = item . Id }) | 40 @Html . ActionLink ( " Delete " , " Delete " , new { id = item . Id }) 41 </ td > 42 </ tr > 43 } 44 </ table > Código CSHTML 6.5: Index.cshtml Teste a mensagem cadastrando uma editora através da url http://localhost:<PORTA_APP>/Editora/Cadastra. Verifique que a mensagem de sucesso aparecerá. 106 www.k19.com.br
CAPÍTULO R OTAS 7 Para acessar uma determinada ação da nossa aplicação, os usuários devem realizar uma requi- sição HTTP utilizando a url correspondente à ação. Por exemplo, para acessar a listagem de edito- ras, é necessário digitar na barra de endereço do navegador a url http://localhost:<PORTA_APP> /Editora/Index. Perceba que o o nome do controlador e o nome da ação desejados fazem parte da url. O formato dessas urls é definido por rotas criadas no arquivo RouteConfig.cs e carregadas no arquivo Global.asax. O código abaixo mostra a rota padrão inserida nos projetos ASP.NET MVC 4. 1 routes . MapRoute ( 2 name : " Default " , 3 url : " { controller }/{ action }/{ id } " , 4 defaults : new { controller = " Home " , action = " Index " , id = UrlParameter . Optional } 5 ); Código C# 7.1: RouteConfig.cs O primeiro argumento do método MapRoute é o nome da rota, o segundo é a expressão que define o formato da rota e o terceiro é o conjunto de valores padrão dos parâmetros da rota. A expressão que determina o formato da rota Default utiliza três parâmetros: controller, action e id. Dessa forma, se o usuário realizar uma requisição HTTP utilizando url http://localhost: <PORTA_APP>/Editora/Remove/1, o ASP.NET MVC criará uma instância do controlador Editora e executará o método Remove() passando o valor 1 como argumento. Basicamente, as rotas associam urls e ações. Veja alguns exemplos de como as urls serão proces- sadas de acordo com as regras de rotas. URL Mapeamento da URL / controller = "Home", action = "Index" /Livro controller = "Livro", action = "Index" /Livro/Adiciona controller = "Livro", action = "Adiciona" /Livro/Remove/1 controller = "Livro", action = "Remove", id = 1 Adicionando uma rota Para acrescentar uma rota, podemos utilizar o método MapRoute(). Suponha que a listagem de editoras deva ser acessada através da url http://localhost:<PORTA_APP>/Catalogo. Nesse caso, podemos adicionar uma rota no arquivo RoutesConfig.cs, como mostrado abaixo. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo " , www.k19.com.br 107
R OTAS 108 5 defaults : new { controller = " Editora " , action = " Index " } 6 ); 7 ... Código C# 7.2: RoutesConfig.cs Importante As rotas são processadas na ordem em que foram inseridas. Portanto, é importante definir as rotas mais específicas antes das rotas mais genéricas. Adicionando Parâmetros nas Rotas Podemos acrescentar parâmetros às rotas. Por exemplo, na rota criada anteriormente para a listagem de editoras, poderíamos adicionar um parâmetro para limitar a quantidade de editoras lis- tadas. 1 routes . MapRoute ( 2 name : " Nova Rota " , 3 url : " Catalogo /{ maximo } " , 4 defaults : new { controller = " Editora " , action = " Index " } 5 ); Código C# 7.3: RoutesConfig.cs Por padrão, os parâmetros adicionados a uma rota são obrigatórios. Dessa forma, no exemplo acima, a listagem de editoras só poderá ser acessada se o valor do parâmetro maximo for definido na url da requisição. Por exemplo, http://localhost:<PORTA_APP>/Catalogo/20. Se uma requisição for realizada para a url http://localhost:<PORTA_APP>/Catalogo, um erro ocorrerá. Para mudar esse comportamento, podemos tornar o parâmetro maximo opcional. Veja o código a seguir. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo /{ maximo } " , 5 defaults : new { controller = " Editora " , action = " Index " , 6 maximo = UrlParameter . Optional } 7 ); 8 ... Código C# 7.4: RoutesConfig.cs Os parâmetros das rotas podem ser recuperados nas ações. Observe o exemplo abaixo. 1 ... 2 public ActionResult Index ( int ? maximo ) 3 { 4 // implementação 5 } 6 ... Código C# 7.5: EditoraController.cs Ao definir parâmetros opcionais, devemos utilizar parâmetros do tipo nullable type nas ações, pois quando o parâmetro não estiver definido na url de uma requisição, o valor null será atribuído 108 www.k19.com.br
109 R OTAS ao parâmetro da ação. Por exemplo, para os tipos int e double, devemos utilizar int? e double?, respectivamente. Exercícios de Fixação 1 Acrescente uma nova rota no projeto K19 para acessarmos a listagem de editoras através da url http://localhost:<PORTA_APP>/Catalogo. Para isso, altere o arquivo RouteConfig.cs da pasta App_Start. Essa nova rota deve ser adiciona antes da rota Default já definida no arquivo RouteCon- fig.cs. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo " , 5 defaults : new { controller = " Editora " , action = " Index " } 6 ); 7 ... Código C# 7.6: RouteConfig.cs Teste essa nova rota, acessando a url http://localhost:<PORTA_APP>/Catalogo. 2 Acrescente um parâmetro chamado prefixo à rota criada no exercício anterior. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo /{ prefixo } " , 5 defaults : new { controller = " Editoras " , action = " Index " } 6 ); 7 ... Código C# 7.7: RouteConfig.cs Faça um teste acessando a url http://localhost:<PORTA_APP>/Catalogo. Verifique que um erro é gerado. Depois tente acessar a url http://localhost:<PORTA_APP>/Catalogo/teste. Verifi- que que a requisição é processada com sucesso, pois o parâmetro prefixo foi definido. 3 Faça o parâmetro prefixo ser opcional. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo /{ prefixo } " , 5 defaults : new { controller = " Editoras " , action = " Index " , 6 prefixo = UrlParameter . Optional } 7 ); 8 ... Código C# 7.8: RouteConfig.cs Agora, tente acessar a url http://localhost:<PORTA_APP>/Catalogo. Nenhum erro é obtido dessa vez. 4 Altere a ação de listagem de editoras para receber o parâmetro prefixo da rota definida anteri- ormente. Defina a lógica para listar as editoras a partir desse parâmetro. www.k19.com.br 109
R OTAS 110 1 ... 2 public ActionResult Index ( string prefixo ) 3 { 4 if ( String . IsNullOrEmpty ( prefixo ) ) 5 { 6 return View ( db . Editoras . ToList () ) ; 7 } 8 else 9 { 10 var consulta = from e in db . Editoras 11 where 12 e . Nome . StartsWith ( prefixo ) 13 select e ; 14 return View ( consulta . ToList () ) ; 15 } 16 } 17 ... Código C# 7.9: EditorasController.cs Para testar, acesse a url http://localhost:<PORTA_APP>/Catalogo/<PREFIXO> com diferentes prefixos. 110 www.k19.com.br
CAPÍTULO VALIDAÇÃO 8 Os usuários podem cometer erros ao preencher um formulário. Por exemplo, esquecer de preen- cher um campo que é obrigatório. Os parâmetros enviados pelos usuários devem ser validados pela aplicação com o intuito de não permitir o armazenamento de informações erradas. Controller O primeiro passo para implementar a validação dos parâmetros enviados através de formulários HTML é definir a lógica de validação na camada de controle. 1 if ( editora . Nome == null || editora . Nome . Trim () . Length == 0) 2 { 3 // Erro de Validação 4 } Código C# 8.1: Definindo as regras de validação O segundo passo é definir mensagens informativas para enviar aos usuários. O ASP.NET MVC possui um objeto especializado no armazenamento de mensagens de erros de validação. Esse objeto pode ser acessado através da propriedade ModelState. 1 if ( editora . Nome == null || editora . Nome . Trim () . Length == 0) 2 { 3 ModelState . AddModelError ( " Nome " , " O campo Nome é obrigatório " ) ; 4 } Código C# 8.2: Definindo as mensagens de erro de validação As mensagens são armazenadas no ModelState através do método AddModelError(). Esse mé- todo permite que as mensagens sejam agrupadas logicamente, pois ele possui dois parâmetros: o primeiro é o grupo da mensagem e o segundo e a mensagem propriamente. O código de validação pode ser colocado nos controladores, mais especificamente nas ações. Se algum erro for encontrado, o fluxo de execução pode ser redirecionado para uma página que mostre as mensagens informativas aos usuários. Normalmente, essa página é a mesma do formulário que foi preenchido incorretamente. O ModelState possui uma propriedade que indica se erros foram adicionados ou não. Essa propriedade chama-se IsValid. 1 [ HttpPost ] 2 public ActionResult Salva ( Editora editora ) 3 { 4 if ( editora . Nome == null || editora . Nome . Trim () . Length == 0) 5 { 6 ModelState . AddModelError ( " Nome " , " O campo Nome é obrigatório . " ) ; 7 } 8 if ( ModelState . IsValid ) 9 { www.k19.com.br 111
VALIDAÇÃO 112 10 db . Editoras . Add ( editora ) ; 11 TempData [ " mensagem " ] = " A editora foi cadastrada com sucesso ! " ; 12 return RedirectToAction ( " Index " ) ; 13 } 14 else 15 { 16 return View ( " Cadastra " ) ; 17 } 18 } Código C# 8.3: EditoraController.cs O ASP.NET MVC também pode adicionar mensagens no ModelState antes do controlador ser chamado. Normalmente, essas mensagens estão relacionadas a erros de conversão. Por exemplo, um campo que espera um número é preenchido com letras. View As mensagens de erros de validação podem ser acrescentadas em uma página através do método ValidationSummary() da propriedade Html. É importante salientar que esse método adiciona todas as mensagens de erro. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Cadastro de Editora " ; 5 } 6 7 < h2 > Cadastro de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary () 11 12 < br / > 13 14 @Html . LabelFor ( model = > model . Nome ) 15 @Html . EditorFor ( model = > model . Nome ) 16 17 < br / > 18 19 @Html . LabelFor ( model = > model . Email ) 20 @Html . EditorFor ( model = > model . Email ) 21 22 < br / > 23 24 < input type = " submit " value = " Enviar " / > 25 } Código CSHTML 8.1: Cadastra.cshtml Podemos utilizar o método ValidationMessageFor() para mostrar somente as mensagens de erro de validação de um determinado grupo. Para não mostrar erros dos grupos com o ValidationSummary(), devemos passar como parâme- tro o valor true. Nesse caso, apenas os erros que não estão associados a um grupo específico serão apresentados pelo ValidationSummary(). 1 @model LivrariaVirtual . Models . Editora 2 3 @{ 112 www.k19.com.br
113 VALIDAÇÃO 4 ViewBag . Title = " Cadastro de Editora " ; 5 } 6 7 < h2 > Cadastro de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 12 < br / > 13 14 @Html . LabelFor ( model = > model . Nome ) 15 @Html . EditorFor ( model = > model . Nome ) 16 @Html . ValidationMessageFor ( model = > model . Nome ) 17 18 < br / > 19 20 @Html . LabelFor ( model = > model . Email ) 21 @Html . EditorFor ( model = > model . Email ) 22 @Html . ValidationMessageFor ( model = > model . Email ) 23 24 < br / > 25 26 < input type = " submit " value = " Enviar " / > 27 } Código CSHTML 8.2: Cadastra.cshtml Exercícios de Fixação 1 Defina as regras de validação dos campos das editoras. A editora deve ter obrigatoriamente nome e email. Para exibir as mensagens sobre os erros de validação, utilize os métodos ValidationMessage() e ValidationMessageFor(). No projeto K19, altera a lógica da ação Salva do controlador Editora. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 if ( String . IsNullOrEmpty ( editora . Nome ) ) 5 { 6 ModelState . AddModelError ( " Nome " , " O nome da editora é obrigatório " ) ; 7 } 8 if ( String . IsNullOrEmpty ( editora . Email ) ) 9 { 10 ModelState . AddModelError ( " Email " , " O email da editora é obrigatório " ) ; 11 } 12 13 if ( ModelState . IsValid ) 14 { 15 db . Editoras . Add ( editora ) ; 16 db . SaveChanges () ; 17 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 18 return RedirectToAction ( " Index " ) ; 19 } 20 else 21 { 22 return View ( " Cadastra " , editora ) ; 23 } 24 } 25 ... Código C# 8.4: EditoraController.cs www.k19.com.br 113
VALIDAÇÃO 114 2 Modifique o formulário de cadastro de editoras. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Cadastra " ; 5 } 6 7 < h2 > Cadastro de Editoras </ h2 > 8 9 @using ( Html . BeginForm ( " Salva " , " Editora " ) ) 10 { 11 @Html . LabelFor ( x = > x . Nome ) 12 @Html . EditorFor ( x = > x . Nome ) 13 @Html . ValidationMessageFor ( x = > x . Nome ) 14 15 < br / > 16 17 @Html . LabelFor ( x = > x . Email ) 18 @Html . EditorFor ( x = > x . Email ) 19 @Html . ValidationMessageFor ( x = > x . Email ) 20 21 < br / > 22 23 < input type = " submit " value = " Enviar " / > 24 } Código CSHTML 8.3: Cadastra.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra. Deixe o campo nome e email em branco e verifique as mensagens de erro de validação. Anotações As lógicas de validação também podem ser definidas através de anotações adicionadas nas clas- ses de modelo. Dessa forma, essas lógicas não estariam mais nos controladores, o que conceitual- mente é o ideal, pois nos controladores só deveria existir lógica para controlar o fluxo da execução. Required Uma das validações mais comuns é a de campo obrigatório. Ela pode ser realizada através da anotação Required. 1 public class Editora 2 { 3 [ Required ] 4 public string Nome { get ; set ;} 5 6 ... 7 } Código C# 8.5: Editora.cs Com essa anotação, a lógica de validação pode ser retirada do controlador Editora. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( Editora editora ) 114 www.k19.com.br
115 VALIDAÇÃO 4 { 5 if ( ModelState . IsValid ) 6 { 7 db . Editoras . Add ( editora ) ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 else 11 { 12 return View ( " Cadastra " , editora ) ; 13 } 14 } 15 ... Código C# 8.6: EditoraController.cs Alterando a mensagem As anotações de validação possuem mensagens padrão que podem ser alteradas através do atri- buto ErrorMessage. 1 ... 2 [ Required ( ErrorMessage = " O campo Nome é obrigatório " ) ] 3 public string Nome { get ; set ; } 4 ... Outros validadores Há outras anotações para validação: • Range • ReqularExpression • StringLength Validação no lado do Cliente As validações podem ser realizadas também nos navegadores para melhorar a interação com os usuários. Antes da terceira versão do ASP .NET MVC, era necessário habilitar a validação no lado do cliente através do método Html.EnableClientValidation(). A partir da terceira versão do ASP.NET MVC, a validação no cliente está habilitada por padrão. Para que a validação no lado cliente funcione corretamente, devemos acrescentar as bibliotecas javascript necessárias. No ASP.NET MVC 4, basta acrescentar a seção de scripts nas páginas. 1 @section Scripts { 2 @Scripts . Render ( " ~/ bundles / jqueryval " ) 3 } www.k19.com.br 115
VALIDAÇÃO 116 Exercícios de Fixação 3 Altere as validações feitas anteriormente, para utilizar DataAnnotations. Lembre-se de alterar todas as mensagens de erro para a língua portuguesa. Acrescente também a validação no cliente. Primeiramente, altere a camada de modelo. 1 using System . ComponentModel . DataAnnotations ; 2 3 namespace K19 . Models 4 { 5 public class Editora 6 { 7 public int Id { get ; set ; } 8 [ Required ( ErrorMessage = " O campo nome é obrigatório " ) ] 9 public string Nome { get ; set ; } 10 [ Required ( ErrorMessage = " O campo email é obrigatório " ) ] 11 public string Email { get ; set ; } 12 } 13 } Código C# 8.8: Editora.cs 4 Altere a ação Salva retirando a lógica de validação definida anteriormente. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 if ( ModelState . IsValid ) 5 { 6 db . Editoras . Add ( editora ) ; 7 db . SaveChanges () ; 8 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 9 return RedirectToAction ( " Index " ) ; 10 } 11 else 12 { 13 return View ( " Cadastra " , editora ) ; 14 } 15 } 16 ... Código C# 8.9: EditoraControlle.cs 5 Para adicionar a validação no lado do cliente, basta alterar a página de cadastro de editoras. Observe o código abaixo. 1 @model K19 . Models . Editora 2 3 < h2 > Cadastro de Editoras </ h2 > 4 @using ( Html . BeginForm ( " Salva " , " Editora " ) ) 5 { 6 @Html . LabelFor ( x = > x . Nome ) 7 @Html . EditorFor ( x = > x . Nome ) 8 @Html . ValidationMessageFor ( x = > x . Nome ) 9 10 < br / > 11 12 @Html . LabelFor ( x = > x . Email ) 13 @Html . EditorFor ( x = > x . Email ) 14 @Html . ValidationMessageFor ( x = > x . Email ) 15 116 www.k19.com.br
117 VALIDAÇÃO 16 < br / > 17 18 < input type = " submit " value = " Enviar " / > 19 20 } 21 @section Scripts { 22 @Scripts . Render ( " ~/ bundles / jqueryval " ) 23 } Código CSHTML 8.5: Cadastra.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra. Deixe o campo nome e email em branco e verifique as mensagens de erro de validação. www.k19.com.br 117
VALIDAÇÃO 118 118 www.k19.com.br
CAPÍTULO S ESSÃO 9 Considere a aplicação de uma loja virtual. Nessa aplicação, os clientes selecionam os produtos desejados e os adiciona no seu carrinho de compra. Cada cliente deve ter o seu próprio carrinho para que os seus produtos não se misturem com os produtos selecionados por outros clientes. A aplicação deve armazenar o carrinho de um cliente até que a compra seja finalizada ou até ela ter certeza que o cliente não precisa mais do carrinho. Para resolver esse problema, podemos utilizar o conceito de Sessão. Para cada navegador co- nectado, o servidor manterá uma sessão aberta. Dessa forma, podemos separar os dados de cada usuário conectado. Identificando os navegadores Para aplicar a ideia de Sessão, é necessário ter a capacidade de identificar o navegador que está requisitando a aplicação a cada requisição. Uma primeira abordagem seria utilizar o endereço IP da máquinas para identificar os navegadores. Porém, nessa abordagem, dois navegadores executando na mesma máquina não poderiam ser identificados individualmente. Outra abordagem é deixar a cargo do servidor a criação de um identificador único para cada navegador conectado. Quando um navegador faz a primeira requisição para a aplicação, o servi- dor deve gerar um identificador único para esse navegador e enviá-lo na resposta HTTP. A partir da segunda requisição, os navegadores devem enviar para a aplicação o identificador recebido na pri- meira requisição. Desta maneira, a aplicação saberá qual é o navegador que está realizando uma requisição. Os navegadores podem enviar os seus respectivos identificadores de diferentes formas. As mais utilizadas são: Reescrita de URL Nesta abordagem, os identificadores são embutidos nos links e botões das páginas da aplicação. Quando os links ou botões são clicados pelo usuário, o identificador é enviado para a aplicação. Uma desvantagem é que todas as páginas devem ser geradas dinamicamente para adicionar o identificador em todos os links e botões. Cookies Cookies são arquivos contendo informações. Eles são gerados nos servidores e enviados para os navegadores. Os navegadores armazenam os cookies localmente na máquina do usuá- rio. Além disso, os navegadores enviam os cookies de volta para o servidor em todas as requi- sições. Os servidores podem armazenar os identificadores gerados em cookies. Dessa forma, a cada requisição, o servidor receberá um cookie contendo o identificador. Sessões no ASP.NET MVC www.k19.com.br 119
S ESSÃO 120 No ASP.NET MVC, o objeto que representa uma sessão é um dicionário. Para armazenar infor- mações, você deve adicionar uma chave e um valor na propriedade Session. Considere um objeto da classe Usuario que agrupa as informações sobre um determinado usuário. O código a seguir é um exemplo de como podemos guardar esse objeto na sessão após a realização da autenticação do usuário. 1 public class LoginController : Controller 2 { 3 ... 4 public ActionResult Login ( Cliente cliente ) 5 { 6 ... 7 Session [ " Cliente " ] = cliente ; 8 ... 9 } 10 } Código C# 9.1: LonginController.cs Você pode adicionar qualquer tipo de valor na sessão. De forma análoga, para resgatar as infor- mações armazenadas, basta acessar a chave correspondente da propriedade Session. Veja o exem- plo a seguir: 1 Cliente cliente = ( Cliente ) Session [ " Cliente " ]; 2 string saudacao = " Bem vindo " + cliente . Nome ; Código C# 9.2: Recuperando informações da sessão Quando um usuário deslogar, podemos eliminar a informação armazenada em sua sessão. Para isso, podemos simplesmente remover todas as chaves do dicionário como no exemplo a seguir. 1 Session . RemoveAll () ; Código C# 9.3: Eliminando todas as informações da sessão Contudo, o método RemoveAll() não elimina a sessão. Ele apenas remove os dados contidos na sessão. Para eliminá-la, você deve utilizar o método Abandon(). Observe o exemplo a seguir. 1 public class LoginController : Controller 2 { 3 ... 4 public ActionResult Logout () 5 { 6 ... 7 Session . Abandon () ; 8 ... 9 } 10 } Código C# 9.4: LoginController.cs Session Mode O ASP.NET MVC disponibiliza quatro modos de sessão (Session Modes): • InProc 120 www.k19.com.br
121 S ESSÃO • StateServer • SQLServer • Custom A diferença entre eles é o modo de armazenamento das sessões. No modo InProc, todas as informações da sessão são armazenadas na memória do servidor. Esse é o modo mais simples e mais utilizado, e vem configurado como o padrão. Uma desvantagem desse modo é a possível sobrecarga de memória se forem armazenadas muitas informações na sessão. Por isso não é indicado para sistemas muito grandes, com muitos usuários navegando simultaneamente. Outro problema é que o servidor da aplicação não pode ser desligado, pois a informação armazenada na memória será perdida. No modo StateServer, as informações são serializadas e enviadas para um servidor indepen- dente do servidor da aplicação. Isto possibilita que o servidor de aplicação possa ser reiniciado sem que as sessões sejam perdidas. Uma desvantagem é o tempo gasto para realizar a serialização e des- serialização. No modo SQLServer, as informações também são serializadas, mas são armazenadas em um banco de dados. Além de permitir que o servidor da aplicação seja reiniciado, este modo possibi- lita um maior controle de segurança dos dados da sessão. Uma desvantagem importante é que o processo é naturalmente lento. No modo Custom, todo o processo de identificação de usuários e armazenamento de sessões pode ser personalizado. Para selecionar algum dos modos disponíveis, basta adicionar a tag <sessionState> dentro da tag <system.web>, no arquivo Web.Config da raiz de um projeto ASP.NET MVC 4. 1 < sessionState mode = " InProc " / > Código XML 9.1: Web.Config Podemos personalizar nossa sessão, modificando alguns atributos da tag <sessionState>. Por exemplo, podemos determinar o tempo de timeout das sessões. Veja o exemplo abaixo. 1 < sessionState mode = " InProc " timeout = " 30 " / > Código XML 9.2: Web.Config Outra possibilidade é desabilitar o uso de cookie com o atributo cookieless. Neste caso será utilizada a reescrita de URL. 1 < sessionState mode = " InProc " cookieless = " true " / > Código XML 9.3: Web.Config Exercícios de Fixação www.k19.com.br 121
S ESSÃO 122 1 Para entender o conceito de sessão, vamos mostrar o nome do usuário logado em todas as páginas. Primeiramente, adicione uma classe chamada Usuario na pasta Models do projeto K19. 1 namespace K19 . Models 2 { 3 public class Usuario 4 { 5 public string Username { get ; set ; } 6 public string Password { get ; set ; } 7 } 8 } Código C# 9.5: Usuario.cs 2 Agora, vamos definir um controlador para implementar o processo de autenticação. Adicione um controlador chamado Autenticador na pasta Controllers do projeto K19. Implemente as ações para o usuário logar-se e deslogar-se do sistema. 1 using System . Web . Mvc ; 2 using K19 . Models ; 3 4 namespace K19 . Controllers 5 { 6 public class AutenticadorController : Controller 7 { 8 9 public ActionResult Formulario () 10 { 11 return View () ; 12 } 13 14 public ActionResult Entrar ( Usuario usuario ) 15 { 16 if ( usuario . Username != null && usuario . Password != null && 17 usuario . Username . Equals ( " K19 " ) && usuario . Password . Equals ( " K19 " ) ) { 18 Session [ " Usuario " ] = usuario ; 19 return RedirectToAction ( " Index " , " Editora " ) ; 20 } 21 else 22 { 23 ViewBag . Mensagem = " usuário ou senha incorretos " ; 24 return View ( " Formulario " ) ; 25 } 26 } 27 28 public ActionResult Sair () 29 { 30 Session . Abandon () ; 31 return RedirectToAction ( " Formulario " ) ; 32 } 33 } 34 } Código C# 9.6: AutenticadorController.cs 3 Crie um arquivo chamado Formulario.cshtml em uma pasta chamada ViewsAutenticador com o seguinte conteúdo. 1 @model K19 . Models . Usuario 2 3 @{ 4 ViewBag . Title = " Formulario " ; 5 } 122 www.k19.com.br
123 S ESSÃO 6 7 @if ( ViewBag . Mensagem != null ) 8 { 9 < h2 > @ViewBag . Mensagem </ h2 > 10 } 11 12 < h2 > Formulario de Autenticação </ h2 > 13 @using ( Html . BeginForm ( " Entrar " , " Autenticador " ) ) 14 { 15 @Html . LabelFor ( x = > x . Username ) 16 @Html . EditorFor ( x = > x . Username ) 17 18 < br / > 19 20 @Html . LabelFor ( x = > x . Password ) 21 @Html . EditorFor ( x = > x . Password ) 22 23 < br / > 24 25 < input type = " submit " value = " Enviar " / > 26 } Código CSHTML 9.1: Formulario.cshtml Faça um teste acessando a url http://localhost:<PORTA_APP>/Autenticador/Formulario. Tente acessar o sistema com usuários ou senhas incorretos. Depois, utilize o usuário K19 e a senha K19. 4 Altere o arquivo K19Layout.cshtml acrescentando links para o usuário logar-se e deslogar-se (caso estiver logado) do sistema. 1 @using K19 . Models 2 3 <! DOCTYPE html > 4 < html > 5 < head > 6 < meta charset = " utf -8 " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 < title > @ViewBag . Title </ title > 9 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 10 @Scripts . Render ( " ~/ bundles / modernizr " ) 11 </ head > 12 < body > 13 < div id = " header " > 14 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 15 16 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 17 18 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 19 20 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 21 22 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 23 24 @if ( Session [ " Usuario " ] != null ) 25 { 26 < span > Olá @ (( Session [ " Usuario " ] as Usuario ) . Username ) ! </ span > 27 @Html . ActionLink ( " Logout " , " Sair " , " Autenticador " ) 28 } 29 else 30 { 31 @Html . ActionLink ( " Login " , " Formulario " , " Autenticador " ) 32 } 33 </ div > 34 @if ( IsSectionDefined ( " Sidebar " ) ) 35 { 36 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 37 } www.k19.com.br 123
S ESSÃO 124 38 else 39 { 40 < div id = " sidebar " > Sidebar padrão </ div > 41 } 42 < div id = " content " > @RenderBody () </ div > 43 < div id = " footer " > K19 Treinamentos </ div > 44 45 @Scripts . Render ( " ~/ bundles / jquery " ) 46 @RenderSection ( " scripts " , required : false ) 47 </ body > 48 </ html > Código CSHTML 9.2: K19Layout.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Autenticador/Formulario. Faça testes logando e deslogando do sistema. 124 www.k19.com.br
CAPÍTULO AUTENTICAÇÃO 10 Muitas vezes, queremos restringir o acesso à determinadas áreas de uma aplicação, seja por ques- tões de segurança ou por organização. Em nossa aplicação poderíamos, por exemplo, definir que para poder adicionar, alterar e remover editoras, o usuário deve estar logado no sistema. Caso con- trário, o usuário apenas poderá listar as editoras. 1 ... 2 public ActionResult Cadastra () 3 { 4 if ( Session [ " Cliente " ] != null ) 5 { 6 return base . View () ; 7 } 8 else 9 { 10 return base . RedirectToAction ( " Index " , " Login " ) ; 11 } 12 } 13 ... Código C# 10.1: EditoraController.cs No exemplo acima, caso um usuário tente adicionar uma editora através do formulário de ca- dastro, a ação Cadastra verificará se o usuário já está logado. Essa verificação é realizada através do uso da sessão, como visto no capítulo 9. Se o usuário não estiver logado, ele será redirecionado para a página de Login. Apesar de funcionar, este código apresenta uma inconveniência. Temos que adicionar essa lógica em todas as ações que queremos que tenha o mesmo comportamento, ou seja, que apenas permitam o acesso de usuários logados. Em outros casos, podemos desejar que algumas ações executem alguma tarefa em comum. Por exemplo, poderíamos adicionar uma mensagem em um arquivo de logging sempre que uma ação fosse executada. Desse modo, poderíamos guardar um histórico sobre o que a aplicação mais execu- tou, qual foi a página mais visitada, etc. Mas novamente, teríamos que adicionar a mesma tarefa em todas as ações da nossa aplicação. Nesses casos, em que várias ações possuem um comportamento em comum, podemos utilizar o conceito de filtros. Um filtro é semelhante a um método que é executado antes ou depois que uma ação. Filtro de Autenticação O ASP.NET MVC já possui alguns filtros prontos para serem utilizados, como o filtro de auten- ticação. Podemos utilizar ele para o nosso primeiro exemplo, onde exigimos que o usuário esteja logado (autenticado) para acessar determinadas áreas da aplicação. Para isso, precisamos adicionar o seguinte código no nosso método de login: www.k19.com.br 125
AUTENTICAÇÃO 126 1 [ dots **] 2 FormsAuthentication . SetAuthCookie ( usuario . Username , false ) ; 3 [ dots **] Código C# 10.2: AutenticadorController.cs Isto adiciona um novo cookie utilizado para a autenticação do usuário. Este novo cookie é in- dependente do cookie utilizado para armazenar informações da sessão. O primeiro parâmetro é referente ao nome do usuário (ou algo que o identifique). O segundo parâmetro é um booleano re- lativo ao tipo do cookie, se é permanente ou não. Caso seja true, ele sempre irá considerar que o usuário está autenticado após a primeira autenticação. Para eliminar o cookie de autenticação, devemos adicionar o seguinte código na acão de logout: 1 ... 2 FormsAuthentication . SignOut () ; 3 ... Código C# 10.3: AutenticadorController.cs As ações que devem utilizar o filtro de autenticação devem ser anotadas com Authorize. 1 ... 2 [ Authorize ] 3 public ActionResult Cadastra () 4 { 5 return base . View () ; 6 } 7 ... Código C# 10.4: EditoraController.cs Se queremos aplicar o mesmo filtro a todas as ações de um controlador, podemos adicionar a notação Authorize na classe. 1 [ Authorize ] 2 public class EditoraController : Controller 3 { 4 ... 5 } Código C# 10.5: EditoraController.cs Desse modo, todas as ações presentes no controlador da Editora exigem que o usuário esteja autenticado. Quando o filtro de autenticação “barra” um usuário de acessar uma página, podemos redirecioná- lo para a página de login. Devemos incluir o seguinte código dentro da tag <system.web> do arquivo Web.Config. 1 < authentication mode = " Forms " > 2 < forms loginUrl = " ~/ Autenticador / Formulario " timeout = " 2880 " / > 3 </ authentication > Código XML 10.1: Web.Config 126 www.k19.com.br
127 AUTENTICAÇÃO Para verificar se o usuário associado à sessão atual está autenticada, podemos utilizar a proprie- dade IsAuthenticated, como a seguir: 1 if ( User . Identity . IsAuthenticated ) 2 { 3 ... 4 } Código C# 10.6: Verificando se o usuário está autenticado Podemos pegar a informação de quem está autenticado através do seguinte comando: 1 string nome = User . Identity . Name ; Código C# 10.7: Verificando quem é o usuário autenticado Isto irá pegar o nome que passamos como parâmetro para o método SetAuthCookie na autenti- cação. Exercícios de Fixação 1 Altere a ação Cadastra do controlador Editora para que somente usuários logados possam acessar o formulário de cadastro. 1 ... 2 public ActionResult Cadastra () 3 { 4 if ( Session [ " Usuario " ] != null ) 5 { 6 return View () ; 7 } 8 else 9 { 10 return RedirectToAction ( " Formulario " , " Autenticador " ) ; 11 } 12 } 13 ... Código C# 10.8: EditorasController.cs Teste acessando o seguinte endereço: http://localhost:<PORTA_APP>/Editora/Cadastra. Ve- rifique que enquanto o login não for efetuado, não será possível acessar o formulário de cadastro. 2 Ao invés de usar sessão, vamos utilizar o filtro de autenticação pronto do ASP.NET MVC. Primeiro, devemos alterar as ações Entrar e Sair do controlador Autenticador. 1 using System . Web . Mvc ; 2 using System . Web . Security ; 3 using K19 . Models ; 4 5 namespace K19 . Controllers 6 { 7 public class AutenticadorController : Controller 8 { 9 10 public ActionResult Formulario () 11 { 12 return View () ; www.k19.com.br 127
AUTENTICAÇÃO 128 13 } 14 15 public ActionResult Entrar ( Usuario usuario ) 16 { 17 if ( usuario . Username != null && usuario . Password != null && 18 usuario . Username . Equals ( " K19 " ) && usuario . Password . Equals ( " K19 " ) ) { 19 20 FormsAuthentication . SetAuthCookie ( usuario . Username , false ) ; 21 return RedirectToAction ( " Index " , " Editora " ) ; 22 } 23 else 24 { 25 ViewBag . Mensagem = " usuário ou senha incorretos " ; 26 return View ( " Formulario " ) ; 27 } 28 } 29 30 public ActionResult Sair () 31 { 32 FormsAuthentication . SignOut () ; 33 return RedirectToAction ( " Formulario " ) ; 34 } 35 } 36 } Código C# 10.9: AutenticadorController.cs 3 Altere o arquivo de layout para mostrar o usuário logado. 1 @using K19 . Models 2 3 <! DOCTYPE html > 4 < html > 5 < head > 6 < meta charset = " utf -8 " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 < title > @ViewBag . Title </ title > 9 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 10 @Scripts . Render ( " ~/ bundles / modernizr " ) 11 </ head > 12 < body > 13 < div id = " header " > 14 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 15 16 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 17 18 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 19 20 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 21 22 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 23 24 @if ( User . Identity . IsAuthenticated ) 25 { 26 < span > Olá @User . Identity . Name </ span > 27 @Html . ActionLink ( " Logout " , " Sair " , " Autenticador " ) 28 } 29 else 30 { 31 @Html . ActionLink ( " Login " , " Formulario " , " Autenticador " ) 32 } 33 </ div > 34 @if ( IsSectionDefined ( " Sidebar " ) ) 35 { 36 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 37 } 38 else 39 { 40 < div id = " sidebar " > Sidebar padrão </ div > 128 www.k19.com.br
129 AUTENTICAÇÃO 41 } 42 < div id = " content " > @RenderBody () </ div > 43 < div id = " footer " > K19 Treinamentos </ div > 44 45 @Scripts . Render ( " ~/ bundles / jquery " ) 46 @RenderSection ( " scripts " , required : false ) 47 </ body > 48 </ html > Código CSHTML 10.1: K19Layout.cshtml 4 Altere o controlador Editora para que somente usuários logados possam acessar o formulário de cadastro. 1 ... 2 [ Authorize ] 3 public ActionResult Cadastra () 4 { 5 return View () ; 6 } 7 ... Código C# 10.10: EditoraController.cs 5 Defina a url para a qual os usuários não logados serão redirecionados ao tentarem acessar o formulário de cadastro de editoras. Altere o arquivo Web.config da raiz do projeto K19. 1 ... 2 < authentication mode = " Forms " > 3 < forms loginUrl = " ~/ Autenticador / Formulario " timeout = " 2880 " / > 4 </ authentication > 5 ... Código XML 10.2: Web.config Acesse o endereço http://localhost:<PORTA_APP>/Editora/Cadastra. Verifique o funciona- mento do filtro de autenticação que redirecionará os usuários que não estão logados para a url defi- nida no arquivo Web.config. www.k19.com.br 129
AUTENTICAÇÃO 130 130 www.k19.com.br
CAPÍTULO T RATAMENTO DE E RROS 11 Inevitavelmente, as aplicações estão sujeitas a erros de várias naturezas. Por exemplo, erros po- dem ser gerados pelo preenchimento incorreto dos campos de um formulário. Esse tipo de erro é causado por falhas dos usuários. Nesse caso, é importante mostrar mensagens informativas com o intuito de fazer o próprio usuário corrigir os valores preenchidos incorretamente. Veja o Capítulo 8. Por outro lado, há erros que não são causados por falhas dos usuários. Por exemplo, um erro de conexão com o banco de dados. Nesses casos, é improvável que os usuários possam fazer algo que resolva o problema. E mesmo que pudessem, provavelmente, não seria conveniente esperar que eles o fizessem. Quando um erro desse tipo ocorre, o ASP.NET MVC cria uma página web com informações so- bre o erro e a envia aos usuários. Para usuários locais, o ASP.NET MVC envia uma página web com informações detalhadas do erro ocorrido. Para usuários remotos, a página web enviada não contém informações detalhadas. Em geral, não é conveniente que os usuários recebam detalhes técnicos sobre os erros gerados por falhas da aplicação. A primeira justificativa é que esses detalhes podem confundir os usuários. Figura 11.1: Exemplo de uma página web com informações sobre um erro www.k19.com.br 131
T RATAMENTO DE E RROS 132 A segunda justificativa é que esses detalhes podem expor alguma falha de segurança da aplicação, deixando-a mais vulnerável a ataques. Try-Catch Os erros de aplicação podem ser identificados através do comando try-catch, que pode ser colocados nos métodos que definem as ações dos controladores. Ao identificar a ocorrência de um erro, os controladores podem devolver uma página web com alguma mensagem para o usuário. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( Editora editora ) 4 { 5 try 6 { 7 db . Editoras . Add ( editora ) ; 8 } 9 catch 10 { 11 return View ( " Error " ) ; 12 } 13 14 return RedirectToAction ( " Index " ) ; 15 } 16 ... Código C# 11.1: EditoraController.cs Podemos criar uma página Error.cshtml na pasta ViewsShared. Dessa forma, todo controlador poderá devolver essa página. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Erro </ title > 9 </ head > 10 < body > 11 < h2 > 12 Servidor com problemas 13 </ h2 > 14 <p > 15 Houve um problema em nosso servidor . < br / > 16 Por favor tente novamente dentro de alguns instantes . 17 </ p > 18 </ body > 19 </ html > Código CSHTML 11.1: Error.cshtml As páginas de erro que serão mostradas pelos controladores teriam uma mensagem simples in- formando que houve um erro na aplicação e que não é possível atender a requisição do usuário naquele momento. Inclusive, seria conveniente padronizar a página de erro. Em outras palavras, todos os controladores teriam que mostrar a mesma página. 132 www.k19.com.br
133 T RATAMENTO DE E RROS Custom Errors Utilizar o comando try-catch nos controladores para lidar com os erros de aplicação não é uma boa alternativa, pois o código do controlador fica mais complexo. Além disso, haveria replicação de código nos controladores, pois provavelmente a página de erro seria padronizada. Para lidar com os erros de aplicação de uma maneira mais prática e fácil de manter, podemos configurar o ASP .NET MVC para utilizar páginas de erro padrão. O primeiro passo é alterar o arquivo de configuração Web.config, acrescentando a tag <customErrors> dentro da tag <system.web>. 1 ... 2 < customErrors mode = " On " > 3 4 </ customErrors > 5 ... Código XML 11.1: Web.Config O atributo mode da tag <customErrors> pode assumir três valores: On: A página de erro padrão será enviada para usuários locais e remotos. Off: A página de erro detalhada será enviada para usuários locais e remotos. RemoteOnly: A página de erro detalhada será enviada para os usuários locais e a padrão para os remotos. Por convenção, o ASP.NET MVC mantém uma página de erro padrão dentro da pasta ViewsShared com o nome Error.cshtml. Vamos alterar este arquivo. O conteúdo da página de erro é basicamente HTML. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Erro </ title > 9 </ head > 10 < body > 11 < h2 > 12 Servidor com problemas 13 </ h2 > 14 <p > 15 Houve um problema no nosso servidor . < br / > 16 Por favor tente novamente dentro de alguns instantes . 17 </ p > 18 </ body > 19 </ html > Código CSHTML 11.2: Error.cshtml Http Errors www.k19.com.br 133
T RATAMENTO DE E RROS 134 Um dos erros mais conhecidos do HTTP é o 404, que ocorre quando o navegador faz uma re- quisição para uma url que não existe. Basicamente, esse erro é gerado por falhas dos usuários ao tentarem digitar diretamente uma url na barra de endereço dos navegadores ou por links ou botões “quebrados” nas páginas da aplicação. Quando o erro 404 ocorre, o ASP .NET MVC utiliza a página padrão para erros de aplicação con- figurada no Web.config através da tag <customErrors>. Porém, esse erro não deve ser considerado um erro de aplicação, pois ele pode ser gerado por falhas do usuário. Ele também não deve ser con- siderado um erro de usuário, pois ele pode ser gerado por falhas da aplicação. Consequentemente, é comum tratar o erro 404 de maneira particular, criando uma página de erro específica para ele. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Arquivo não encontrado ! </ title > 9 </ head > 10 < body > 11 < h2 > 12 Esse arquivo não foi encontrado . Verifique se a url está correta . 13 </ h2 > 14 </ body > 15 </ html > Código CSHTML 11.3: NotFound.cshtml No arquivo de configuração, podemos especificar uma página web particular para o erro 404 ou para os outros erros do HTTP. 1 ... 2 < customErrors mode = " On " > 3 < error statusCode = " 404 " redirect = " ~/ ErrorPage / NotFound " / > 4 </ customErrors > 5 ... Código XML 11.2: Web.Config De acordo com o código anterior, devemos definir um controlador chamado ErrorPage com uma ação chamada NotFound. Essa ação será acionada toda vez que o erro 404 do HTTP ocorrer. 1 namespace K19 . Controllers 2 { 3 public class ErrorPageController : Controller 4 { 5 public ActionResult NotFound () 6 { 7 return View () ; 8 } 9 } 10 } Código C# 11.2: ErroPageController.cs Exercícios de Fixação 134 www.k19.com.br
135 T RATAMENTO DE E RROS 1 Crie uma nova ação no controlador Teste do projeto K19, conforme o código abaixo. 1 ... 2 public ActionResult TestaErro () 3 { 4 string [] nomes = new string [] { " Jonas Hirata " , " Rafael Cosentino " }; 5 string nome = nomes [2]; 6 return View () ; 7 } 8 ... Código C# 11.3: TesteController.cs Acesse o seguinte endereço http://localhost:<PORTA_APP>/Teste/TestaErro e verifique a tela de erro. Observação: No Visual Web Developer, quando executamos a aplicação, ele a executa em modo debug. Dessa forma, toda vez que um erro for gerado no processamento de uma requisição, a exe- cução da aplicação é suspensa no ponto em que o erro ocorreu e detalhes sobre o problema são apresentados. Para continuar a execução da aplicação após a ocorrência de um erro, aperte a tecla F5. 2 Trate o erro do exercício anterior com o bloco try-catch e redirecione o usuário para uma tela com a seguinte mensagem “Sistema Temporariamente Indisponível”. Altere a ação TestaErro do controlador Teste. 1 ... 2 public ActionResult TestaErro () 3 { 4 try 5 { 6 string [] nomes = new string [] { " Jonas Hirata " , " Rafael Cosentino " }; 7 string nome = nomes [2]; 8 return View () ; 9 } 10 catch 11 { 12 return View ( " Error " ) ; 13 } 14 } 15 ... Código C# 11.4: TesteController.cs 3 Altere o código do arquivo Error.cshtml da pasta ViewsShared conforme o código abaixo: 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < meta name = " viewport " content = " width = device - width " / > 9 < title > Error </ title > 10 </ head > 11 < body > 12 < h2 > 13 Sistema Temporariamente Indisponível ... 14 </ h2 > www.k19.com.br 135
T RATAMENTO DE E RROS 136 15 </ body > 16 </ html > Código CSHTML 11.4: Error.cshtml Acesse novamente o endereço http://localhost:<PORTA_APP>/Teste/TestaErro e verifique a nova página de erro. 4 Remova o bloco try-catch da ação TestaErro do controlador Teste que você adicionou no exercício anterior. 1 ... 2 public ActionResult TestaErro () 3 { 4 string [] nomes = new string [] { " Jonas Hirata " , " Rafael Cosentino " }; 5 string nome = nomes [2]; 6 return View () ; 7 } 8 ... Código C# 11.5: TesteController.cs 5 Altere o arquivo Web.config para configurar o ASP.NET MVC para utilizar páginas de erro pa- drão. 1 ... 2 < system . web > 3 < customErrors mode = " On " > 4 </ customErrors > 5 ... 6 < system . web > 7 ... Código XML 11.3: Web.config Acesse o endereço http://localhost:<PORTA_APP>/Teste/TestaErro e verifique a página de erro. 6 Vamos definir uma página para ser exibida quando o erro 404 ocorrer. Primeiramente, crie um controlador chamado ErrorPage com uma ação chamada NotFound. 1 namespace K19 . Controllers 2 { 3 public class ErrorPageController : Controller 4 { 5 public ActionResult NotFound () 6 { 7 return View () ; 8 } 9 } 10 } Código C# 11.6: ErroPageController.cs 7 Na pasta ViewsErrorPage do projeto K19, adicione um arquivo chamado NotFound.cshtml com o seguinte conteúdo. 1 @{ 2 Layout = null ; 3 } 136 www.k19.com.br
137 T RATAMENTO DE E RROS 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Arquivo não encontrado ! </ title > 9 </ head > 10 < body > 11 < h2 > 12 Esse arquivo não foi encontrado . Verifique se a url está correta . 13 </ h2 > 14 </ body > 15 </ html > Código CSHTML 11.5: NotFound.cshtml 8 Altere o arquivo Web.Config para definir que quando um erro 404 ocorrer, a página exibida deve ser aquela que acabamos de criar. 1 ... 2 < system . web > 3 < customErrors mode = " On " > 4 < error statusCode = " 404 " redirect = " ~/ ErrorPage / NotFound " / > 5 </ customErrors > 6 ... 7 < system . web > 8 ... Código XML 11.4: Web.config Tente acessar páginas da aplicação que não existam. Acesse, por exemplo, o endereço http: //localhost:<PORTA_APP>/PaginaInexistente. www.k19.com.br 137
T RATAMENTO DE E RROS 138 138 www.k19.com.br
APÊNDICE ASP.NET W EB API A Para permitir a implementação de webservices restful, o ASP.NET Web API foi adicionado à quarta versão do ASP .NET MVC. Webservices restful são baseados no estilo arquitetural REST. Discutiremos, a seguir, os principais conceitos desse estilo arquitetural. REST Resources No estilo arquitetural REST, toda informação disponível é chamada de resource. Por exemplo, uma imagem é um resource. Uma lista de produtos é um resource. O cadastro de uma pessoa é um resource. URIs Todo resource possui um identificador único globalmente. Os identificadores são utililzados para acessar os respectivos resources. Particularmente, em uma rede HTTP, os resources são identificados por URIs(Uniform Resource Identifier - http://tools.ietf.org/html/rfc3986). Por exemplo, a URI www.k19.com.br/cursos identifica na internet a lista de cursos da K19. Media Types Um resource pode ser apresentado em diversos formatos. Na arquitetura REST, esses formatos são chamados de media type. Considere o cadastro de uma pessoa disponível em uma rede HTTP . Eventualmente, esse cadastro pode ser apresentado em html, xml ou json. 1 < html > 2 < head > 3 < title > Rafael Cosentino </ title > 4 < head > 5 6 < body > 7 < h1 > Rafael Cosentino </ h1 > 8 <p > Líder de treinamentos da K19 </ p > 9 </ body > 10 </ html > 1 < pessoa > 2 < nome > Rafael Cosentino </ nome > 3 < descricao > Líder de treinamentos da K19 </ descricao > 4 < pessoa > www.k19.com.br 139
ASP.NET W EB API 140 1 { " nome " : " Rafael Cosentino " , " descricao " : " Líder de treinamentos da K19 " } Operações Em uma arquitetura REST, um conjunto pequeno e fixo de operações deve ser definido previa- mente. As operações são utilizadas para manipular os recursos de alguma forma. Por exemplo, em uma rede HTTP os recursos são manipulados pelos métodos do protocolo , HTTP. Podemos atribuir uma semântica diferente para cada um desses métodos. Resource Método HTTP Semântica www.k19.com.br/cursos GET pega a lista de cursos www.k19.com.br/cursos POST adiciona um curso na lista Resources No ASP.NET Web API, os resources são definidos por controladores de uma aplicação ASP.NET MVC que derivam de System.Web.Http.ApiController. 1 public class CursosController : ApiController 2 { 3 ... 4 } Código C# A.1: CursosController.cs URIs As URIs dos resources são definidas através de rotas do ASP.NET Web API criadas no arquivo RouteConfig.cs. O Visual Web Developer adiciona a seguinte rota padrão do ASP.NET Web API nos projetos ASP.NET MVC 4: 1 routes . MapHttpRoute ( 2 name : " DefaultApi " , 3 routeTemplate : " api /{ controller }/{ id } " , 4 defaults : new { id = RouteParameter . Optional } 5 ); Código C# A.2: RouteConfig.cs De acordo com a rota padrão do ASP.NET Web API, a URI do resource correspondente ao contro- lador Cursos é http://localhost:<PORTA_APP>/api/cursos. 140 www.k19.com.br
141 ASP.NET W EB API Operações Podemos associar as operações HTTP aos métodos da classe CursosController. Essas associa- ções são estabelecidas automaticamente através dos nomes dos métodos. Por exemplo, a operação GET do HTTP é associada automaticamente a um método cujo nome possui o prefixo Get. Veja o exemplo abaixo. 1 public class CursosController : ApiController 2 { 3 ... 4 // GET http :// localhost : < PORTA_APP >/ api / cursos 5 public Curso [] GetCursos () 6 { 7 ... 8 } 9 ... 10 } Código C# A.3: CursosController.cs Na rota padrão do ASP .NET Web API, um parâmetro opcional chamado id foi definido. Quando esse parâmetro estiver presente em uma requisição do tipo GET, podemos recuperar o valor dele em um método cujo nome possua o prefixo Get e tenha um argumento chamado id. Observe o código abaixo. 1 public class CursosController : ApiController 2 { 3 ... 4 // GET http :// localhost : < PORTA_APP >/ api / cursos /5 5 public Curso [] GetCurso ( int id ) 6 { 7 ... 8 } 9 ... 10 } Código C# A.4: CursosController.cs Também podemos recuperar parâmetros de URL. Para isso, basta definir argumentos com os mesmos nomes desses parâmetros nos métodos associados às operações do HTTP. Veja o exemplo abaixo 1 public class CursosController : ApiController 2 { 3 ... 4 // GET http :// localhost : < PORTA_APP >/ api / cursos ? sigla = K32 5 public Curso [] GetCurso ( string sigla ) 6 { 7 ... 8 } 9 ... 10 } Código C# A.5: CursosController.cs Considere o código a seguir. 1 public class CursosController : ApiController www.k19.com.br 141
ASP.NET W EB API 142 2 { 3 ... 4 // POST http :// localhost : < PORTA_APP >/ api / cursos 5 public void PostCurso ( Curso curso ) 6 { 7 ... 8 } 9 ... 10 } Código C# A.6: CursosController.cs O método PostCurso() será associado a operação POST do HTTP. Os valores dos parâmetros en- viados dentro de uma requisição do tipo POST à url http://localhost:<PORTA_APP>/api/cursos serão armazenados automaticamente pelo ASP.NET Web API nas propriedades do argumento curso do método PostCurso() de acordo com o nome dessas propriedades e dos nomes dos parâmetros enviados. Por exemplo, o valor do parâmetro Nome será armazenado dentro da propriedade Nome caso ambos existam. Media Type Por padrão, o ASP NET Web API utiliza os headers Content-type e Accept para definir o media type dos dados de entrada e saída. No exemplo anterior, se uma requisição do tipo GET for realizada à url //POSThttp://localhost:<PORTA_APP>/api/cursos com header Accept: application/json, o resource correspondente será apresentado em formato JSON. Exercícios de Fixação 1 Vamos definir o nosso primeiro webservice utilizando web api. Para isso, adicione uma classe para modelar cursos na pasta Models. 1 namespace K19 . Models 2 { 3 public class Curso 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public int CargaHoraria { get ; set ; } 8 } 9 } Código C# A.7: Curso.cs 2 Agora crie um controlador chamado Cursos para implementar as operações do webservice. Na criação desse controlador, selecione o template Empty API Controller. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Net ; 5 using System . Net . Http ; 6 using System . Web . Http ; 7 using LivrariaVirtual . Models ; 8 using System . Web . Mvc ; 9 142 www.k19.com.br
143 ASP.NET W EB API 10 namespace K19 . Controllers 11 { 12 public class CursosController : ApiController 13 { 14 private static List < Curso > cursos = new List < Curso > 15 { 16 new Curso { 17 Id = 1 , 18 Sigla = " K31 " , 19 Nome = " C # e Orientação a Objetos " 20 }, 21 new Curso { 22 Id = 2 , 23 Sigla = " K32 " , 24 Nome = " Desenvolvimento Web com ASP . NET MVC " } 25 }; 26 27 public List < Curso > Get () 28 { 29 return cursos ; 30 } 31 32 public Curso Get ( string sigla ) 33 { 34 var consulta = from c in cursos 35 where c . Sigla . Equals ( sigla ) 36 select c ; 37 38 if ( consulta . Count () == 0) 39 { 40 return null ; 41 } 42 else 43 { 44 return consulta . First () ; 45 } 46 47 } 48 49 public Curso Get ( int id ) 50 { 51 var consulta = from c in cursos 52 where c . Id == id 53 select c ; 54 55 if ( consulta . Count () == 0) 56 { 57 return null ; 58 } 59 else 60 { 61 return consulta . First () ; 62 } 63 } 64 65 public void Post ( Curso curso ) 66 { 67 cursos . Add ( curso ) ; 68 } 69 70 } 71 } Código C# A.8: CursosController.cs 3 Para testar o webservice que retorna a lista de cursos, basta acessar o seguinte endereço: http: www.k19.com.br 143
ASP.NET W EB API 144 //localhost:<PORTA_APP>/api/cursos. 4 Teste o webservice que devolve o curso a partir de uma sigla. Basta acessar o seguinte endereço: http://localhost:<PORTA_APP>/api/cursos?sigla=K31. 5 Teste o webservice que devolve um curso a partir de um id. Basta acessar o seguinte endereço: http://localhost:<PORTA_APP>/api/cursos/1. 6 Defina uma página com um formulário para testar o webservice que adiciona curso. Para isso, crie um controlador TestaWebServiceCurso conforme o código abaixo: 1 using System . Web . Mvc ; 2 3 namespace K19 . Controllers 4 { 5 public class TestaWebServiceCursoController : Controller 6 { 7 public ActionResult Formulario () 8 { 9 return View () ; 10 } 11 } 12 } Código C# A.9: TestaWebServiceCursoController.cs 7 Para testar o webservice que adiciona curso, devemos definir a página Formulario.cshtml. 1 @model K19 . Models . Curso 2 @{ 3 ViewBag . Title = " Formulario " ; 4 } 5 6 < h2 > Formulario </ h2 > 7 8 @using ( Html . BeginForm ( null , null , FormMethod . Post , new { @action = " / api / cursos " }) ) 9 { 10 < fieldset > 11 < legend > Curso </ legend > 12 < div class = " editor - label " > 13 @Html . LabelFor ( model = > model . Id ) 14 </ div > 15 < div class = " editor - field " > 16 @Html . EditorFor ( model = > model . Id ) 17 </ div > 18 < div class = " editor - label " > 19 @Html . LabelFor ( model = > model . Sigla ) 20 </ div > 21 < div class = " editor - field " > 22 @Html . EditorFor ( model = > model . Sigla ) 23 </ div > 24 < div class = " editor - label " > 25 @Html . LabelFor ( model = > model . Nome ) 26 </ div > 27 < div class = " editor - field " > 28 @Html . EditorFor ( model = > model . Nome ) 29 </ div > 30 <p > 31 < input type = " submit " value = " Create " / > 32 </ p > 33 </ fieldset > 34 } 35 @section Scripts { 36 @Scripts . Render ( " ~/ bundles / jqueryval " ) 37 } 144 www.k19.com.br
145 ASP.NET W EB API Código CSHTML A.1: Formulario.cshtml 8 Teste o webservice que adiciona curso através do formulário criado no exercício anterior. Acesse o formulário através do seguinte endereço: http://localhost:<PORTA_APP>/TestaWebServiceCurso/ Formulario. Para verificar se o curso foi adicionado, acesse http://localhost:<PORTA_APP>/api/ cursos/. www.k19.com.br 145
ASP.NET W EB API 146 146 www.k19.com.br
APÊNDICE M IGRATIONS B O Entity Framework Code First da Microsoft tem uma funcionalidade que permite controlar as mudanças no banco de dados que serão realizadas de acordo com as alterações na camada de mo- delo da aplicação. Essa funcionalidade é chamada de Code First Migrations. O Code First Migrations foi adicionado na versão 4.3.1 do Entity Framework. Neste capítulo uti- lizaremos o Entity Framework 5.0 RC. Passo a Passo Para testar o funcionamento do Code First Migrations, criaremos um projeto no Visual Studio. Agora, devemos instalar o Entity Framework 5.0 RC. Para isso, utilizaremos o gerenciador de pa- cotes Nuget do Visual Studio. www.k19.com.br 147
M IGRATIONS 148 O Nuget permite que bibliotecas e ferramentas sejam instaladas no Visual Studio. Ao instalar pacotes no seu projeto, ele adiciona as bibliotecas, referências e faz as alterações necessárias nos ar- quivos de configuração. Mais informações sobre o Nuget e os pacotes disponíveis, acesse o endereço http://nuget.org/packages Para instalar o Entity Framework 5.0 RC, basta executar o seguinte comando no Package Manager Console. Install-Package EntityFramework -Pre Após a instalação do Entity Framework 5.0 RC, adicionaremos uma classe de modelo chamada Editora. Essa entidade será mapeada através de uma classe chamada Livraria. Para testar, criare- mos uma classe com o método Main(). Observe o código dessas três classes. 1 namespace EFMigrations 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 148 www.k19.com.br
149 M IGRATIONS 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 } 9 } Código C# B.1: Editora.cs 1 namespace EFMigrations 2 { 3 public class Livraria : DbContext 4 { 5 public DbSet < Editora > Editoras { get ; set ; } 6 } 7 } Código C# B.2: Livraria.cs 1 namespace EFMigrations 2 { 3 class Program 4 { 5 static void Main ( string [] args ) 6 { 7 using ( Livraria livraria = new Livraria () ) 8 { 9 Editora e = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 10 livraria . Editoras . Add ( e ) ; 11 livraria . SaveChanges () ; 12 } 13 } 14 } 15 } Código C# B.3: Program.cs Após a execução do projeto, teremos a seguinte tabela e banco de dados. O próximo passo é alterar a classe de modelo Editora. 1 namespace EFMigrations 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 www.k19.com.br 149
M IGRATIONS 150 9 public string Telefone { get ; set ; } 10 } 11 } Código C# B.4: Editora.cs Com a alteração na classe Editora, um exeção é gerada ao executar o projeto. Para resolver esse problema, o banco de dados precisa ser atualizado. Para fazer essa atualização, o Entity Framework recomenda a utilização da ferramenta Code First Migrations. O primeiro passo para utilizar Code First Migrations é habilitá-lo e adicioná-lo ao projeto através do Package Manager Console. O comando visto acima adiciona uma pasta chamada Migrations no projeto. Esta pasta contém dois arquivos. • A classe Configuration permite definir o comportamento do Code First Migrations para o nosso contexto. • A classe parcial InitialCreate define a primeira versão das tabelas do banco de dados. O Code First Migration tem dois comandos fundamentais. • Add-Migration que gera o código necessário para atualizar o banco de dados de acordo com as alterações nas classes de modelo. 150 www.k19.com.br
151 M IGRATIONS • Update-Database aplica as alterações pendentes no banco de dados. Como adicionamos a propriedade Telefone na classe de modelo Editora, devemos criar e exe- cutar uma migração para atualizar o banco de dados. Para criar uma migração, devemos utilizar o comando Add-Migration. Para executar a migração, devemos utilizar o comando Update-Database. Observe a execução do comando Add-Migration. A classe que define a migração é adicionada na pasta Migrations Para aplicar a migração, devemos executar o comando Update-Database. No banco de dados, uma coluna é adicionada na tabela Editoras. www.k19.com.br 151
M IGRATIONS 152 Podemos também adicionar uma nova entidade. Considere a seguinte classe para definir os li- vros da nossa aplicação. 1 namespace EFMigrations 2 { 3 public class Livro 4 { 5 public int Id { get ; set ; } 6 public string Titulo { get ; set ; } 7 public decimal Preco { get ; set ; } 8 public int EditoraId { get ; set ; } 9 public Editora Editora { get ; set ; } 10 } 11 } Código C# B.5: Livro.cs Para estabelecer o relacionamento entre editoras e livros, a classe Editora deve ser alterada. 1 namespace EFMigrations 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 public string Telefone { get ; set ; } 9 10 [ ForeignKey ( " EditoraId " ) ] 11 public virtual IList < Livro > Livros { get ; set ; } 12 } 13 } Código C# B.6: Editora.cs A classe livro deve ser mapeada no contexto Livraria. 1 namespace EFMigrations 2 { 3 public class Livraria : DbContext 4 { 5 public DbSet < Editora > Editoras { get ; set ; } 6 public DbSet < Livro > Livros { get ; set ; } 7 } 8 } 152 www.k19.com.br
153 M IGRATIONS Código C# B.7: Livraria.cs Após a alteração das classes Editora e Livraria e a criação da entidade Livro, devemos criar uma migração para atualizar o banco de dados. Para aplicar a migração, devemos utilizar o comando Update-Database. A tabela correspondente à classe Livro e a chave estrangeira para realizar o relacionamento entre livros e editoras são adicionadas no banco de dados. www.k19.com.br 153
M IGRATIONS 154 Exercícios de Fixação 1 Para testar o Code First Migrations, altere a classe Editora do projeto K19. Acrescente nessa classe uma propriedade chamada Telefone. 1 using System . ComponentModel . DataAnnotations ; 2 3 namespace K19 . Models 4 { 5 public class Editora 6 { 7 public int Id { get ; set ; } 8 9 [ Required ( ErrorMessage = " O campo nome é obrigatório " ) ] 10 public string Nome { get ; set ; } 11 12 [ Required ( ErrorMessage = " O campo email é obrigatório " ) ] 13 public string Email { get ; set ; } 14 15 public string Telefone { get ; set ; } 16 } 17 } Código C# B.8: Editora.cs 2 Acesse o seguinte endereço: http://localhost:<PORTA_APP>/Editora. Verifique que uma exceção do tipo System.InvalidOperationException ocorrerá. 154 www.k19.com.br
155 M IGRATIONS 3 Para corrigir o problema visto no exercício anterior, devemos habilitar o Code First Migrations. Para isso, execute o seguinte comando através do Package Manager Console. Enable-Migrations. www.k19.com.br 155
M IGRATIONS 156 Verifique que uma pasta chamada Migrations com dois arquivos: <DATA>_InitialCreate.cs e Con- figuration.cs foi gerada no projeto K19. 4 Após habilitar o Code First Migrations no projeto K19, devemos adicionar uma migração para atualizar o banco de dados adicionando a coluna Telefone na tabela Editoras. Crie uma migração chamada AddTelefoneToEditora através do comando Add-Migration. Execute este comando através do Package Manager Console. Verifique que um arquivo de migração foi criado na pasta Migrations: 1 namespace K19 . Migrations 2 { 3 using System ; 4 using System . Data . Entity . Migrations ; 5 6 public partial class AddTelefoneToEditora : DbMigration 7 { 8 public override void Up () 9 { 10 AddColumn ( " dbo . Editoras " , " Telefone " , c = > c . String () ) ; 11 } 12 13 public override void Down () 156 www.k19.com.br
157 M IGRATIONS 14 { 15 DropColumn ( " dbo . Editoras " , " Telefone " ) ; 16 } 17 } 18 } Código C# B.9: <DATA>_AddTelefoneToEditora.cs 5 Para atualizar a tabela Editoras no banco de dados, utilize o comando Update-Database que é executado através do Package Manager Console. Verifique que a coluna Telefone foi adicionada a tabela Editoras. 6 Defina uma classe Livro na pasta Models conforme o código abaixo: 1 namespace K19 . Models 2 { 3 public class Livro 4 { 5 public int Id { get ; set ; } 6 public string Titulo { get ; set ; } 7 public decimal Preco { get ; set ; } 8 public int EditoraId { get ; set ; } 9 public Editora Editora { get ; set ; } 10 } www.k19.com.br 157
M IGRATIONS 158 11 } Código C# B.10: Livro.cs 7 Altere a classe K19Context para mapear a classe Livro para uma tabela no banco de dados. 1 using System . Data . Entity ; 2 3 namespace K19 . Models 4 { 5 public class K19Context : DbContext 6 { 7 public DbSet < Editora > Editoras { get ; set ; } 8 public DbSet < Livro > Livros { get ; set ; } 9 } 10 } Código C# B.11: K19Context.cs 8 Para gerar a tabela Livros no banco de dados, devemos criar uma migração. Crie uma migração chamada AddLivro através do comando Add-Migration. 9 Execute o comando Update-Database através do Package Manager Console para gerar a tabela referente a classe Livro no banco de dados. 158 www.k19.com.br
159 M IGRATIONS Verifique que a tabela referente a classe Livro foi gerada no banco de dados. www.k19.com.br 159
M IGRATIONS 160 160 www.k19.com.br
APÊNDICE P ROJETO C Nos capítulos anteriores, vimos os recursos do ASP .NET MVC e do Entity Framework. Agora, vamos solidificar os conhecimentos obtidos e, além disso, mostraremos alguns padrões e conceitos relacionados ao desenvolvimento de aplicações web. Como exemplo de aplicação desenvolveremos uma aplicação de cadastro de jogadores e seleções de futebol. Modelo Por onde começar o desenvolvimento de uma aplicação? Essa é uma questão recorrente. Um ótimo ponto de partida é desenvolver as entidades principais da aplicação. No nosso caso, vamos nos res- tringir às entidades Selecao e Jogador. Devemos estabelecer um relacionamento entre essas entida- des já que um jogador atua em uma seleção. Exercícios de Fixação 1 Crie um projeto do tipo ASP .NET MVC 4 Web Application chamado K19-CopaDoMundo se- guindo os passos vistos no exercício do capítulo 4. 2 Adicione na pasta Models as seguintes classes. 1 namespace K19CopaDoMundo . Models 2 { 3 public class Selecao 4 { 5 public int Id { get ; set ; } 6 public string Pais { get ; set ; } 7 public string Tecnico { get ; set ; } 8 } 9 } Código C# C.1: Selecao.cs 1 namespace K19CopaDoMundo . Models 2 { 3 public class Jogador 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Posicao { get ; set ; } 8 public DateTime Nascimento { get ; set ; } 9 public double Altura { get ; set ; } 10 public int SelecaoId { get ; set ; } 11 public Selecao Selecao { get ; set ; } 12 } 13 } www.k19.com.br 161
P ROJETO 162 Código C# C.2: Jogador.cs Persistência - Mapeamento Depois de definir algumas entidades podemos começar o processo de implementação da persis- tência da nossa aplicação. Vamos aplicar os recursos do Entity Framework - Code First que apren- demos nos primeiros capítulos. Inicialmente, vamos definir o mapeamento das nossas entidades através de uma classe derivada de DbContext e acrescentar as propriedades referentes a chave pri- mária e chave estrangeira. Exercícios de Fixação 3 Adicione as seguintes propriedades e anotações as classes Selecao e Jogador. 1 using System . ComponentModel . DataAnnotations . Schema ; 2 3 namespace K19CopaDoMundo . Models 4 { 5 [ Table ( " Selecoes " ) ] 6 public class Selecao 7 { 8 public int Id { get ; set ; } 9 public string Pais { get ; set ; } 10 public string Tecnico { get ; set ; } 11 public virtual List < Jogador > Jogadores { get ; set ; } 12 } 13 } Código C# C.3: Selecao.cs 1 using System . ComponentModel . DataAnnotations . Schema ; 2 3 namespace K19CopaDoMundo . Models 4 { 5 [ Table ( " Jogadores " ) ] 6 public class Jogador 7 { 8 public int Id { get ; set ; } 9 public string Nome { get ; set ; } 10 public string Posicao { get ; set ; } 11 public DateTime Nascimento { get ; set ; } 12 public double Altura { get ; set ; } 13 public int SelecaoId { get ; set ; } 14 [ InverseProperty ( " Jogadores " ) ] 15 public virtual Selecao Selecao { get ; set ; } 16 } 17 } Código C# C.4: Jogador.cs 4 Adicione a classe K19CopaDoMundoContext a pasta Models. 162 www.k19.com.br
163 C.3. P ERSISTÊNCIA - M APEAMENTO 1 using System . Data . Entity ; 2 3 namespace K19CopaDoMundo . Models 4 { 5 public class K19CopaDoMundoContext : DbContext 6 { 7 public DbSet < Selecao > Selecoes { get ; set ; } 8 public DbSet < Jogador > Jogadores { get ; set ; } 9 } 10 } Código C# C.5: K19CopaDoMundoContext.cs Persistência - Configuração Precisamos definir a nossa string de conexão para que a nossa aplicação utilize a base de dados k19copadomundo como padrão. Exercícios de Fixação 5 Acrescente ao arquivo Web.config, que fica na raiz do projeto, a string de conexão. 1 < connectionStrings > 2 < add 3 name = " K19CopaDoMundoContext " providerName = " System . Data . SqlClient " 4 connectionString = " Server =. SQLEXPRESS ; Database = k19copadomundo ; 5 User Id = sa ; Password = sa ; Trusted_Connection = False ; MultipleActiveResultSets = True " / > 6 </ connectionStrings > Código XML C.1: Web.config Persistência - Repositórios Vamos deixar os repositórios para acessar as entidades da nossa aplicação preparados. Os repositó- rios precisam de DbContexts para realizar as operações de persistência. Então, cada repositório terá um construtor para receber um DbContext como parâmetro. Exercícios de Fixação 6 Crie uma classe na pasta Models chamada SelecaoRepository. 1 using System ; 2 using System . Collections . Generic ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 public class SelecaoRepository : IDisposable 7 { 8 private bool disposed = false ; 9 10 private K19CopaDoMundoContext context ; 11 12 public SelecaoRepository ( K19CopaDoMundoContext context ) 13 { www.k19.com.br 163
P ROJETO 164 14 this . context = context ; 15 } 16 17 public void Adiciona ( Selecao selecao ) 18 { 19 context . Selecoes . Add ( selecao ) ; 20 } 21 22 public List < Selecao > Selecoes 23 { 24 get 25 { 26 return context . Selecoes . ToList () ; 27 } 28 } 29 public void Salva () 30 { 31 context . SaveChanges () ; 32 } 33 34 protected virtual void Dispose ( bool disposing ) 35 { 36 if (! this . disposed ) 37 { 38 if ( disposing ) 39 { 40 context . Dispose () ; 41 } 42 } 43 this . disposed = true ; 44 } 45 46 public void Dispose () 47 { 48 Dispose ( true ) ; 49 GC . SuppressFinalize ( this ) ; 50 } 51 } 52 } Código C# C.6: SelecaoRepository.cs 7 Analogamente crie um repositório de jogadores. 1 using System ; 2 using System . Collections . Generic ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 public class JogadorRepository : IDisposable 7 { 8 private bool disposed = false ; 9 private K19CopaDoMundoContext context ; 10 11 public JogadorRepository ( K19CopaDoMundoContext context ) 12 { 13 this . context = context ; 14 } 15 16 public void Adiciona ( Jogador jogador ) 17 { 18 context . Jogadores . Add ( jogador ) ; 19 } 20 21 public List < Jogador > Jogadores 22 { 23 get { return context . Jogadores . ToList () ; } 24 } 25 164 www.k19.com.br
165 C.3. P ERSISTÊNCIA - M APEAMENTO 26 public void Salva () 27 { 28 context . SaveChanges () ; 29 } 30 31 protected virtual void Dispose ( bool disposing ) 32 { 33 if (! this . disposed ) 34 { 35 if ( disposing ) 36 { 37 context . Dispose () ; 38 } 39 } 40 this . disposed = true ; 41 } 42 43 public void Dispose () 44 { 45 Dispose ( true ) ; 46 GC . SuppressFinalize ( this ) ; 47 } 48 } 49 } Código C# C.7: JogadorRepository.cs Unit of Work O único propósito de criar uma classe UnitOfWork é ter certeza que quando temos múltiplos repositórios eles compartilham o mesmo DbContext. Para isto, devemos apenas criar um método Salva e uma propriedade para cada repositório. Exercícios de Fixação 8 Crie uma classe UnitOfWork na pasta Models. 1 using System ; 2 using System . Collections . Generic ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 public class UnitOfWork : IDisposable 7 { 8 private bool disposed = false ; 9 private K19CopaDoMundoContext context = new K19CopaDoMundoContext () ; 10 private SelecaoRepository selecaoRepository ; 11 private JogadorRepository jogadorRepository ; 12 13 14 public JogadorRepository JogadorRepository 15 { 16 get 17 { 18 if ( jogadorRepository == null ) 19 { 20 jogadorRepository = new JogadorRepository ( context ) ; 21 } 22 return jogadorRepository ; 23 } 24 } www.k19.com.br 165
P ROJETO 166 25 26 public SelecaoRepository SelecaoRepository 27 { 28 get 29 { 30 if ( selecaoRepository == null ) 31 { 32 selecaoRepository = new SelecaoRepository ( context ) ; 33 } 34 return selecaoRepository ; 35 } 36 } 37 38 public void Salva () 39 { 40 context . SaveChanges () ; 41 } 42 43 protected virtual void Dispose ( bool disposing ) 44 { 45 if (! this . disposed ) 46 { 47 if ( disposing ) 48 { 49 context . Dispose () ; 50 } 51 } 52 this . disposed = true ; 53 } 54 55 public void Dispose () 56 { 57 Dispose ( true ) ; 58 GC . SuppressFinalize ( this ) ; 59 } 60 } 61 } Código C# C.8: UnitOfWork.cs Apresentação - Template Vamos definir um template para as telas da nossa aplicação. Aplicaremos algumas regras CSS para melhorar a parte visual das telas. Exercícios de Fixação 9 Na pasta Content, altere o arquivo Site.css acrescentando algumas regras CSS. 1 . logo 2 { 3 vertical - align : middle ; 4 } 5 6 . botao 7 { 8 background - color : #064 D83 ; 9 margin : 0 0 0 20 px ; 10 color : white ; 11 text - decoration : none ; 12 font - size : 20 px ; 13 line - height : 20 px ; 14 padding : 5 px ; 166 www.k19.com.br
167 C.3. P ERSISTÊNCIA - M APEAMENTO 15 vertical - align : middle ; 16 } 17 18 . botao : hover 19 { 20 background - color : # cccccc ; 21 color : #666666; 22 } 23 24 . formulario fieldset 25 { 26 float : left ; 27 margin : 0 0 20 px 0; 28 border : 1 px solid #333333; 29 } 30 31 . formulario fieldset legend 32 { 33 color : #064 D83 ; 34 font - weight : bold ; 35 } 36 37 . botao - formulario 38 { 39 background - color : #064 D83 ; 40 color : # ffffff ; 41 padding : 5 px ; 42 vertical - align : middle ; 43 border : none ; 44 } 45 46 . titulo 47 { 48 color : #064 D83 ; 49 clear : both ; 50 } 51 52 . tabela 53 { 54 border : 1 px solid #064 D83 ; 55 border - collapse : collapse ; 56 } 57 58 . tabela tr th 59 { 60 background - color : #064 D83 ; 61 color : # ffffff ; 62 } 63 64 . tabela tr th , . tabela tr td 65 { 66 border : 1 px solid #064 D83 ; 67 padding : 2 px 5 px ; 68 } 69 70 71 /* Styles for validation helpers 72 -- ------ ------ ------- ------ ------ ------ ------ ------ ------ -- */ 73 . field - validation - error 74 { 75 color : # ff0000 ; 76 } 77 78 . field - validation - valid 79 { 80 display : none ; 81 } 82 83 . input - validation - error 84 { www.k19.com.br 167
P ROJETO 168 85 border : 1 px solid # ff0000 ; 86 background - color : # ffeeee ; 87 } 88 89 . validation - summary - errors 90 { 91 font - weight : bold ; 92 color : # ff0000 ; 93 } 94 95 . validation - summary - valid 96 { 97 display : none ; 98 } Código CSS C.1: Site.css 10 Copie o arquivo k19-logo.png da pasta K19-Arquivos da sua Área de Trabalho para a pasta Images. 11 Agora altere o arquivo _Layout.cshtml. 1 <! DOCTYPE html > 2 < html lang = " en " > 3 < head > 4 < meta charset = " utf -8 " / > 5 < title > Copa do Mundo </ title > 6 < link href = " ~/ __TemplateIcon . ico " rel = " shortcut icon " type = " image /x - icon " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 9 @Scripts . Render ( " ~/ bundles / modernizr " ) 10 </ head > 11 < body > 12 < header > 13 < div class = " content - wrapper " > 14 < div class = " float - left " > 15 <p class = " site - title " > < img class = " logo " 16 src = " ~/ Images / k19 - logo . jpg " alt = " K19 Logo " / > </ p > </ div > 17 < div class = " float - right " > 18 < nav > 19 < ul id = " menu " > 20 < li > @Html . ActionLink ( " Selecoes " , " Index " , " Selecao " , null , ← new { @class = " botao " }) </ li > 21 < li > @Html . ActionLink ( " Jogadores " , " Index " , " Jogador " , null , new { ← @class = " botao " }) </ li > 22 </ ul > 23 </ nav > 24 </ div > 25 </ div > 26 </ header > 27 < div id = " body " > 28 @RenderSection ( " featured " , required : false ) 29 < section class = " content - wrapper main - content clear - fix " > 30 @RenderBody () 31 </ section > 32 </ div > 33 < footer > 34 < div class = " content - wrapper " > 35 < div class = " float - left " > 36 <p >& copy ; @DateTime . Now . Year - K19 Copa do Mundo </ p > 37 </ div > 38 < div class = " float - right " > 39 < ul id = " social " > 40 < li > <a href = " http :// facebook . com / k19treinamentos " class = " ← facebook " > Facebook - K19 Treinamentos </ a > </ li > 41 < li > <a href = " http :// twitter . com / k19treinamentos " class = " ← twitter " > Twitter - K19 Treinamentos </ a > </ li > 42 </ ul > 168 www.k19.com.br
169 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 43 </ div > 44 </ div > 45 </ footer > 46 @Scripts . Render ( " ~/ bundles / jquery " ) 47 @RenderSection ( " scripts " , required : false ) 48 </ body > 49 </ html > Código CSHTML C.1: _Layout.cshtml Cadastrando e Listando Seleções Na tela de seleções, vamos adicionar um formulário para cadastrar novas seleções e uma tabela para apresentar as já cadastradas. Aplicaremos regras de validação específicas para garantir que ne- nhum dado incorreto seja armazenado no banco de dados. Exercícios de Fixação 12 Para cadastrar a seleção, devemos definir o controlador. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Data ; 4 using System . Data . Entity ; 5 using System . Linq ; 6 using System . Web ; 7 using System . Web . Mvc ; 8 using K19CopaDoMundo . Models ; 9 10 namespace K19CopaDoMundo . Controllers 11 { 12 public class SelecaoController : Controller 13 { 14 private UnitOfWork unitOfWork = new UnitOfWork () ; 15 16 public ActionResult Create () 17 { 18 return View () ; 19 } 20 21 protected override void Dispose ( bool disposing ) 22 { 23 unitOfWork . Dispose () ; 24 base . Dispose ( disposing ) ; 25 } 26 } 27 } Código C# C.9: SelecaoController.cs 13 Vamos criar uma tela Create.cshtml para cadastrar as seleções. Adicione o arquivo a pasta Views/Selecoes com o seguinte conteúdo. 1 @model K19CopaDoMundo . Models . Selecao 2 3 @{ 4 ViewBag . Title = " Create " ; www.k19.com.br 169
P ROJETO 170 5 } 6 7 < h2 > Create </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 12 < fieldset > 13 < legend > Selecao </ legend > 14 15 < div class = " editor - label " > 16 @Html . LabelFor ( model = > model . Pais ) 17 </ div > 18 < div class = " editor - field " > 19 @Html . EditorFor ( model = > model . Pais ) 20 @Html . ValidationMessageFor ( model = > model . Pais ) 21 </ div > 22 23 < div class = " editor - label " > 24 @Html . LabelFor ( model = > model . Tecnico ) 25 </ div > 26 < div class = " editor - field " > 27 @Html . EditorFor ( model = > model . Tecnico ) 28 @Html . ValidationMessageFor ( model = > model . Tecnico ) 29 </ div > 30 31 <p > 32 < input type = " submit " value = " Create " / > 33 </ p > 34 </ fieldset > 35 } 36 37 < div > 38 @Html . ActionLink ( " Listagem de Seleções " , " Index " ) 39 </ div > 40 41 @section Scripts { 42 @Scripts . Render ( " ~/ bundles / jqueryval " ) 43 } Código CSHTML C.2: Create.cshtml 14 O próximo passo é definir a action que irá salvar a seleção no nosso banco de dados. Devemos também acrescentar as validações a nossa entidade. 1 [ HttpPost ] 2 public ActionResult Create ( Selecao selecao ) 3 { 4 if ( ModelState . IsValid ) 5 { 6 unitOfWork . SelecaoRepository . Adiciona ( selecao ) ; 7 unitOfWork . Salva () ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 return View ( selecao ) ; 11 } Código C# C.10: SelecaoController.cs 1 using System . ComponentModel . DataAnnotations . Schema ; 2 using System . ComponentModel . DataAnnotations ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 [ Table ( " Selecoes " ) ] 7 public class Selecao 8 { 170 www.k19.com.br
171 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 9 public int Id { get ; set ; } 10 [ Required ( ErrorMessage = " O campo Pais é obrigatório . " ) ] 11 public string Pais { get ; set ; } 12 [ Required ( ErrorMessage = " O campo Tecnico é 13 obrigatório . " ) ] public string Tecnico { get ; set ; } 14 public virtual List < Jogador > Jogadores { get ; set ; } 15 } 16 } Código C# C.11: Selecao.cs 15 Defina a action e a página para listar todas as entidades de seleção. 1 public ActionResult Index () 2 { 3 return View ( unitOfWork . SelecaoRepository . Selecoes ) ; 4 } Código C# C.12: SelecaoController.cs 1 @model IEnumerable < K19CopaDoMundo . Models . Selecao > 2 3 @{ 4 ViewBag . Title = " Index " ; 5 } 6 7 < h2 > Index </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Create New " , " Create " ) 11 </ p > 12 < table > 13 < tr > 14 < th > 15 @Html . DisplayNameFor ( model = > model . Pais ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Tecnico ) 19 </ th > 20 < th > </ th > 21 </ tr > 22 23 @foreach ( var item in Model ) { 24 < tr > 25 < td > 26 @Html . DisplayFor ( modelItem = > item . Pais ) 27 </ td > 28 < td > 29 @Html . DisplayFor ( modelItem = > item . Tecnico ) 30 </ td > 31 </ tr > 32 } 33 34 </ table > Código CSHTML C.3: Index.cshtml 16 Vamos definir a tela de listagem de Seleções como a página principal do nosso site. Altere a rota padrão no arquivo RouteConfig.cs. 1 routes . MapRoute ( 2 name : " Default " , 3 url : " { controller }/{ action }/{ id } " , 4 defaults : new { controller = " Selecao " , action = " Index " , 5 id = UrlParameter . Optional } ) ; www.k19.com.br 171
P ROJETO 172 Código C# C.13: RouteConfig.cs Removendo Seleções Vamos acrescentar a funcionalidade de remover seleções. Exercícios de Fixação 17 Acrescente uma coluna na tabela de listagem de seleções. 1 @model IEnumerable < K19CopaDoMundo . Models . Selecao > 2 3 @{ 4 ViewBag . Title = " Index " ; 5 } 6 7 < h2 > Index </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Create New " , " Create " ) 11 </ p > 12 < table > 13 < tr > 14 < th > 15 @Html . DisplayNameFor ( model = > model . Pais ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Tecnico ) 19 </ th > 20 < th > </ th > 21 </ tr > 22 23 @foreach ( var item in Model ) { 24 < tr > 25 < td > 26 @Html . DisplayFor ( modelItem = > item . Pais ) 27 </ td > 28 < td > 29 @Html . DisplayFor ( modelItem = > item . Tecnico ) 30 </ td > 31 < td > 32 @Html . ActionLink ( " Remover " ," Delete " , new { id = item . Id }) 33 </ td > 34 </ tr > 35 } 36 37 </ table > Código CSHTML C.4: Index.cshtml 18 Defina um método Busca na classe SelecaoRepository que retorna uma entidade seleção a partir de um parâmetro id. 1 public Selecao Busca ( int id ) 2 { 3 return context . Selecoes . Find ( id ) ; 4 } 172 www.k19.com.br
173 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES Código C# C.14: SelecaoRepository.cs 19 Defina uma action Delete que irá mostrar a tela de confirmação de remoção da entidade. 1 public ActionResult Delete ( int id ) 2 { 3 Selecao selecao = unitOfWork . SelecaoRepository . Busca ( id ) ; 4 return View ( selecao ) ; 5 } Código C# C.15: SelecaoController.cs 20 Defina a tela de confirmação de remoção da seleção. 1 @model K19CopaDoMundo . Models . Selecao 2 3 @{ 4 ViewBag . Title = " Delete " ; 5 } 6 7 < h2 > Remoção de Seleção </ h2 > 8 9 < h3 > Você tem certeza que deseja remover esta seleção ? </ h3 > 10 < fieldset > 11 < legend > Selecao </ legend > 12 13 < div class = " display - label " > 14 @Html . DisplayNameFor ( model = > model . Pais ) 15 </ div > 16 < div class = " display - field " > 17 @Html . DisplayFor ( model = > model . Pais ) 18 </ div > 19 20 < div class = " display - label " > 21 @Html . DisplayNameFor ( model = > model . Tecnico ) 22 </ div > 23 < div class = " display - field " > 24 @Html . DisplayFor ( model = > model . Tecnico ) 25 </ div > 26 </ fieldset > 27 @using ( Html . BeginForm () ) { 28 <p > 29 < input type = " submit " value = " Delete " / > | 30 @Html . ActionLink ( " Listagem De Seleções " , " Index " ) 31 </ p > 32 } Código CSHTML C.5: Delete.cshtml 21 Defina um método na classe SelecaoRepository que remove uma entidade seleção a partir de um parâmetro id. 1 public void Remove ( int id ) 2 { 3 Selecao selecao = Busca ( id ) ; 4 context . Selecoes . Remove ( selecao ) ; 5 } Código C# C.16: SelecaoRepository.cs 22 Defina a action que remove a seleção do banco de dados. www.k19.com.br 173
P ROJETO 174 1 [ HttpPost ] 2 [ ActionName ( " Delete " ) ] 3 public ActionResult DeleteConfirmed ( int id ) 4 { 5 unitOfWork . SelecaoRepository . Remove ( id ) ; 6 unitOfWork . Salva () ; 7 return RedirectToAction ( " Index " ) ; 8 } Código C# C.17: SelecoesController.cs Cadastrando, Listando e Removendo Jogadores Na tela de jogadores, vamos adicionar um formulário para cadastrar novos jogadores e uma tabela para apresentar os já cadastrados. Aplicaremos regras de validação específicas para garantir que ne- nhum dado incorreto seja armazenado no banco de dados. Exercícios de Fixação 23 Para cadastrar o jogador, devemos definir o controlador. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Data ; 4 using System . Data . Entity ; 5 using System . Linq ; 6 using System . Web ; 7 using System . Web . Mvc ; 8 using K19CopaDoMundo . Models ; 9 10 namespace K19CopaDoMundo . Controllers 11 { 12 public class JogadorController : Controller 13 { 14 private UnitOfWork unitOfWork = new UnitOfWork () ; 15 16 public ActionResult Create () 17 { 18 ViewBag . SelecaoId = new SelectList ( unitOfWork . SelecaoRepository . Selecoes , " Id " , ← " Pais " ) ; 19 return View () ; 20 } 21 22 protected override void Dispose ( bool disposing ) 23 { 24 unitOfWork . Dispose () ; 25 base . Dispose ( disposing ) ; 26 } 27 } 28 } Código C# C.18: JogadorController.cs 24 Vamos criar uma tela Create.cshtml para cadastrar os jogadores. Adicione o arquivo a pasta Views/Jogador com o seguinte conteúdo. 1 @model K19CopaDoMundo . Models . Jogador 2 3 @{ 174 www.k19.com.br
175 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 4 ViewBag . Title = " Create " ; 5 } 6 7 < h2 > Create </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 12 < fieldset > 13 < legend > Jogador </ legend > 14 15 < div class = " editor - label " > 16 @Html . LabelFor ( model = > model . Nome ) 17 </ div > 18 < div class = " editor - field " > 19 @Html . EditorFor ( model = > model . Nome ) 20 @Html . ValidationMessageFor ( model = > model . Nome ) 21 </ div > 22 23 < div class = " editor - label " > 24 @Html . LabelFor ( model = > model . Posicao ) 25 </ div > 26 < div class = " editor - field " > 27 @Html . EditorFor ( model = > model . Posicao ) 28 @Html . ValidationMessageFor ( model = > model . Posicao ) 29 </ div > 30 31 < div class = " editor - label " > 32 @Html . LabelFor ( model = > model . Nascimento ) 33 </ div > 34 < div class = " editor - field " > 35 @Html . EditorFor ( model = > model . Nascimento ) 36 @Html . ValidationMessageFor ( model = > model . Nascimento ) 37 </ div > 38 39 < div class = " editor - label " > 40 @Html . LabelFor ( model = > model . Altura ) 41 </ div > 42 < div class = " editor - field " > 43 @Html . EditorFor ( model = > model . Altura ) 44 @Html . ValidationMessageFor ( model = > model . Altura ) 45 </ div > 46 47 < div class = " editor - label " > 48 @Html . LabelFor ( model = > model . SelecaoId ) 49 </ div > 50 < div class = " editor - field " > 51 @Html . DropDownList ( " SelecaoId " , String . Empty ) 52 @Html . ValidationMessageFor ( model = > model . SelecaoId ) 53 </ div > 54 55 <p > 56 < input type = " submit " value = " Create " / > 57 </ p > 58 </ fieldset > 59 } 60 61 < div > 62 @Html . ActionLink ( " Listagem de Jogadores " , " Index " ) 63 </ div > 64 65 @section Scripts { 66 @Scripts . Render ( " ~/ bundles / jqueryval " ) 67 } Código CSHTML C.6: Create.cshtml 25 O próximo passo é definir a action que irá salvar o jogador no nosso banco de dados. Devemos também acrescentar as validações a nossa entidade. www.k19.com.br 175
P ROJETO 176 1 [ HttpPost ] 2 public ActionResult Create ( Jogador jogador ) 3 { 4 if ( ModelState . IsValid ) 5 { 6 unitOfWork . JogadorRepository . Adiciona ( jogador ) ; 7 unitOfWork . Salva () ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 ViewBag . SelecaoId = new SelectList ( unitOfWork . SelecaoRepository . Selecoes , ← " Id " , " Pais " ) ; 11 return View () ; 12 } Código C# C.19: JogadorController.cs 1 using System . ComponentModel . DataAnnotations . Schema ; 2 using System . ComponentModel . DataAnnotations ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 [ Table ( " Jogadores " ) ] 7 public class Jogador 8 { 9 public int Id { get ; set ; } 10 [ Required ( ErrorMessage = " O campo Nome é obrigatório . " ) ] 11 public string Nome { get ; set ; } 12 [ Required ( ErrorMessage = " O campo Posicao é 13 obrigatório . " ) ] public string Posicao { get ; set ; } 14 [ Required ( ErrorMessage = " O campo Nascimento é 15 obrigatório . " ) ] 16 [ DataType ( DataType . Date ) ] 17 public DateTime Nascimento { get ; set ; } 18 [ Required ( ErrorMessage = " O campo Altura é 19 obrigatório . " ) ] 20 public double Altura { get ; set ; } 21 public int SelecaoId { get ; set ; } 22 [ InverseProperty ( " Jogadores " ) ] 23 public virtual Selecao Selecao { get ; set ; } 24 } 25 } Código C# C.20: Jogador.cs 26 Defina a action e a página para listar todas as entidades de jogador. 1 public ActionResult Index () 2 { 3 return View ( unitOfWork . JogadorRepository . Jogadores ) ; 4 } Código C# C.21: JogadorController.cs 1 @model IEnumerable < K19CopaDoMundo . Models . Jogador > 2 3 @{ 4 ViewBag . Title = " Listagem de Jogadores " ; 5 } 6 7 < h2 > Listagem de Jogadores </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Cadastrar Jogador " , " Create " ) 11 </ p > 12 < table > 13 < tr > 176 www.k19.com.br
177 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 14 < th > 15 @Html . DisplayNameFor ( model = > model . Nome ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Posicao ) 19 </ th > 20 < th > 21 @Html . DisplayNameFor ( model = > model . Nascimento ) 22 </ th > 23 < th > 24 @Html . DisplayNameFor ( model = > model . Altura ) 25 </ th > 26 < th > 27 @Html . DisplayNameFor ( model = > model . SelecaoId ) 28 </ th > 29 < th > </ th > 30 </ tr > 31 32 @foreach ( var item in Model ) { 33 < tr > 34 < td > 35 @Html . DisplayFor ( modelItem = > item . Nome ) 36 </ td > 37 < td > 38 @Html . DisplayFor ( modelItem = > item . Posicao ) 39 </ td > 40 < td > 41 @Html . DisplayFor ( modelItem = > item . Nascimento ) 42 </ td > 43 < td > 44 @Html . DisplayFor ( modelItem = > item . Altura ) 45 </ td > 46 < td > 47 @Html . DisplayFor ( modelItem = > item . SelecaoId ) 48 </ td > 49 </ tr > 50 } 51 52 </ table > Código CSHTML C.7: Index.cshtml Removendo Jogadores Vamos acrescentar a funcionalidade de remover jogadores. Exercícios de Fixação 27 Acrescente uma coluna na tabela de listagem de jogadores. 1 @model IEnumerable < K19CopaDoMundo . Models . Jogador > 2 3 @{ 4 ViewBag . Title = " Listagem de Jogadores " ; 5 } 6 7 < h2 > Listagem de Jogadores </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Cadastrar Jogador " , " Create " ) 11 </ p > 12 < table > 13 < tr > www.k19.com.br 177
P ROJETO 178 14 < th > 15 @Html . DisplayNameFor ( model = > model . Nome ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Posicao ) 19 </ th > 20 < th > 21 @Html . DisplayNameFor ( model = > model . Nascimento ) 22 </ th > 23 < th > 24 @Html . DisplayNameFor ( model = > model . Altura ) 25 </ th > 26 < th > 27 @Html . DisplayNameFor ( model = > model . SelecaoId ) 28 </ th > 29 < th > </ th > 30 </ tr > 31 32 @foreach ( var item in Model ) { 33 < tr > 34 < td > 35 @Html . DisplayFor ( modelItem = > item . Nome ) 36 </ td > 37 < td > 38 @Html . DisplayFor ( modelItem = > item . Posicao ) 39 </ td > 40 < td > 41 @Html . DisplayFor ( modelItem = > item . Nascimento ) 42 </ td > 43 < td > 44 @Html . DisplayFor ( modelItem = > item . Altura ) 45 </ td > 46 < td > 47 @Html . DisplayFor ( modelItem = > item . SelecaoId ) 48 </ td > 49 < td > @Html . ActionLink ( " Remover " , " Delete " , 50 new { id = item . Id }) </ td > </ tr > 51 } 52 </ table > Código CSHTML C.8: Index.cshtml 28 Defina um método Busca na classe JogadorRepository que retorna uma entidade jogador a partir de um parâmetro id. 1 public Jogador Busca ( int id ) 2 { 3 return context . Jogadores . Find ( id ) ; 4 } Código C# C.22: JogadorRepository.cs 29 Defina uma action Delete que irá mostrar a tela de confirmação de remoção da entidade. 1 public ActionResult Delete ( int id ) 2 { 3 Jogador jogador = unitOfWork . JogadorRepository . Busca ( id ) ; 4 return View ( jogador ) ; 5 } Código C# C.23: JogadorController.cs 30 Defina a tela de confirmação de remoção do jogador. 1 @model K19CopaDoMundo . Models . Jogador 178 www.k19.com.br
179 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 2 3 @{ 4 ViewBag . Title = " Remoção do Jogador " ; 5 } 6 7 < h2 > Remoção do Jogador </ h2 > 8 9 < h3 > Você tem certeza que deseja remover este jogador ? </ h3 > 10 < fieldset > 11 < legend > Jogador </ legend > 12 13 < div class = " display - label " > 14 @Html . DisplayNameFor ( model = > model . Nome ) 15 </ div > 16 < div class = " display - field " > 17 @Html . DisplayFor ( model = > model . Nome ) 18 </ div > 19 20 < div class = " display - label " > 21 @Html . DisplayNameFor ( model = > model . Posicao ) 22 </ div > 23 < div class = " display - field " > 24 @Html . DisplayFor ( model = > model . Posicao ) 25 </ div > 26 27 < div class = " display - label " > 28 @Html . DisplayNameFor ( model = > model . Nascimento ) 29 </ div > 30 < div class = " display - field " > 31 @Html . DisplayFor ( model = > model . Nascimento ) 32 </ div > 33 34 < div class = " display - label " > 35 @Html . DisplayNameFor ( model = > model . Altura ) 36 </ div > 37 < div class = " display - field " > 38 @Html . DisplayFor ( model = > model . Altura ) 39 </ div > 40 41 < div class = " display - label " > 42 @Html . DisplayNameFor ( model = > model . SelecaoId ) 43 </ div > 44 < div class = " display - field " > 45 @Html . DisplayFor ( model = > model . SelecaoId ) 46 </ div > 47 </ fieldset > 48 @using ( Html . BeginForm () ) { 49 <p > 50 < input type = " submit " value = " Delete " / > | 51 @Html . ActionLink ( " Listagem de Jogadores " , " Index " ) 52 </ p > 53 } Código CSHTML C.9: Delete.cshtml 31 Defina um método na classe JogadorRepository que remove uma entidade jogador a partir de um parâmetro id. 1 public void Remove ( int id ) 2 { 3 Jogador jogador = context . Jogadores . Find ( id ) ; 4 context . Jogadores . Remove ( jogador ) ; 5 } Código C# C.24: JogadorRepository.cs 32 Defina a action que remove o jogador do banco de dados. www.k19.com.br 179
P ROJETO 180 1 [ HttpPost ] 2 [ ActionName ( " Delete " ) ] 3 public ActionResult DeleteConfirmed ( int id ) 4 { 5 unitOfWork . JogadorRepository . Remove ( id ) ; 6 unitOfWork . Salva () ; 7 return RedirectToAction ( " Index " ) ; 8 } Código C# C.25: JogadoresController.cs Membership e Autorização Na maioria dos casos, as aplicações devem controlar o acesso dos usuários. Vamos implementar um mecanismo de autenticação na nossa aplicação utilizando filtro e Membership. As requisições feitas pelos usuários passarão pelo filtro. A função do filtro é verificar se o usuário está logado ou não. Se estiver logado o filtro autoriza o acesso. Caso contrário, o filtro redirecionará o usuário para a tela de login. Exercícios de Fixação 33 Adicione a seguinte classe a pasta Models: 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web . Mvc ; 5 using System . ComponentModel . DataAnnotations ; 6 7 namespace K19CopaDoMundo . Models 8 { 9 10 public class ChangePasswordModel 11 { 12 [ Required ] 13 [ DataType ( DataType . Password ) ] 14 [ Display ( Name = " Senha " ) ] 15 public string OldPassword { get ; set ; } 16 17 [ Required ] 18 [ StringLength (100 , ErrorMessage = " O {0} deve ter no mínimo {2} caracteres . " , ← MinimumLength = 6) ] 19 [ DataType ( DataType . Password ) ] 20 [ Display ( Name = " Nova senha " ) ] 21 public string NewPassword { get ; set ; } 22 23 [ DataType ( DataType . Password ) ] 24 [ Display ( Name = " Confirmação de senha " ) ] 25 [ Compare ( " NewPassword " , ErrorMessage = " A senha e a confirmação não conferem . " ← )] 26 public string ConfirmPassword { get ; set ; } 27 } 28 29 public class LoginModel 30 { 31 [ Required ] 32 [ Display ( Name = " Usuário " ) ] 33 public string UserName { get ; set ; } 34 35 [ Required ] 36 [ DataType ( DataType . Password ) ] 180 www.k19.com.br
181 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 37 [ Display ( Name = " Senha " ) ] 38 public string Password { get ; set ; } 39 40 [ Display ( Name = " Lembrar ? " ) ] 41 public bool RememberMe { get ; set ; } 42 } 43 44 public class RegisterModel 45 { 46 [ Required ] 47 [ Display ( Name = " Usuário " ) ] 48 public string UserName { get ; set ; } 49 50 [ Required ] 51 [ DataType ( DataType . EmailAddress ) ] 52 [ Display ( Name = " Email " ) ] 53 public string Email { get ; set ; } 54 55 [ Required ] 56 [ StringLength (100 , ErrorMessage = " O {0} deve ter no mínimo {2} caracteres . " , ← MinimumLength = 6) ] 57 [ DataType ( DataType . Password ) ] 58 [ Display ( Name = " Senha " ) ] 59 public string Password { get ; set ; } 60 61 [ DataType ( DataType . Password ) ] 62 [ Display ( Name = " Confirmação de senha " ) ] 63 [ Compare ( " Password " , ErrorMessage = " A senha e a confirmação não conferem . " ) ] 64 public string ConfirmPassword { get ; set ; } 65 } 66 67 } Código C# C.26: Usuario.cs 34 Acrescente a seguinte classe a pasta Controllers. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web ; 5 using System . Web . Mvc ; 6 using System . Web . Security ; 7 using K19CopaDoMundo . Models ; 8 9 namespace K19CopaDoMundo . Controllers 10 { 11 public class UsuarioController : Controller 12 { 13 // 14 // GET : / Usuario / Login 15 16 public ActionResult Login () 17 { 18 return View () ; 19 } 20 21 // 22 // POST : / Usuario / Login 23 24 [ HttpPost ] 25 public ActionResult Login ( LoginModel model , string returnUrl ) 26 { 27 if ( ModelState . IsValid ) 28 { 29 if ( Membership . ValidateUser ( model . UserName , model . Password ) ) 30 { 31 FormsAuthentication . SetAuthCookie ( model . UserName , model . RememberMe ) ; 32 if ( Url . IsLocalUrl ( returnUrl ) && returnUrl . Length > 1 && returnUrl . ← www.k19.com.br 181
P ROJETO 182 StartsWith ( " / " ) 33 && ! returnUrl . StartsWith ( " // " ) && ! returnUrl . StartsWith ( " / " ) ) 34 { 35 return Redirect ( returnUrl ) ; 36 } 37 else 38 { 39 return RedirectToAction ( " Index " , " Selecao " ) ; 40 } 41 } 42 else 43 { 44 ModelState . AddModelError ( " " , " O usuário e / ou a senha está incorreto . " ) ; 45 } 46 } 47 48 49 return View ( model ) ; 50 } 51 52 // 53 // GET : / Usuario / LogOff 54 55 public ActionResult LogOff () 56 { 57 FormsAuthentication . SignOut () ; 58 59 return Redirect ( " / " ) ; 60 } 61 62 // 63 // GET : / Usuario / Register 64 65 public ActionResult Register () 66 { 67 return View () ; 68 } 69 70 // 71 // POST : / Usuario / Register 72 73 [ HttpPost ] 74 public ActionResult Register ( RegisterModel model ) 75 { 76 if ( ModelState . IsValid ) 77 { 78 // Attempt to register the user 79 MembershipCreateStatus createStatus ; 80 Membership . CreateUser ( model . UserName , model . Password , model . Email , null , null , ← true , null , out createStatus ) ; 81 82 if ( createStatus == MembershipCreateStatus . Success ) 83 { 84 FormsAuthentication . SetAuthCookie ( model . UserName , false /* ← createPersistentCookie */ ) ; 85 return Redirect ( " / " ) ; 86 } 87 else 88 { 89 ModelState . AddModelError ( " " , ErrorCodeToString ( createStatus ) ) ; 90 } 91 } 92 93 94 return View ( model ) ; 95 } 96 97 // 98 // GET : / Usuario / ChangePassword 99 182 www.k19.com.br
183 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 100 [ Authorize ] 101 public ActionResult ChangePassword () 102 { 103 return View () ; 104 } 105 106 // 107 // POST : / Usuario / ChangePassword 108 109 [ Authorize ] 110 [ HttpPost ] 111 public ActionResult ChangePassword ( ChangePasswordModel model ) 112 { 113 if ( ModelState . IsValid ) 114 { 115 116 117 bool changePasswordSucceeded ; 118 try 119 { 120 MembershipUser currentUser = Membership . GetUser ( User . Identity . Name , true /* ← userIsOnline */ ) ; 121 changePasswordSucceeded = currentUser . ChangePassword ( model . OldPassword , ← model . NewPassword ) ; 122 } 123 catch ( Exception ) 124 { 125 changePasswordSucceeded = false ; 126 } 127 128 if ( changePasswordSucceeded ) 129 { 130 return RedirectToAction ( " ChangePasswordSuccess " ) ; 131 } 132 else 133 { 134 ModelState . AddModelError ( " " , " A senha atual ou a confirmação está incorreta . ← "); 135 } 136 } 137 138 139 return View ( model ) ; 140 } 141 142 // 143 // GET : / Usuario / ChangePasswordSuccess 144 145 public ActionResult ChangePasswordSuccess () 146 { 147 return View () ; 148 } 149 150 private IEnumerable < string > GetErrorsFromModelState () 151 { 152 return ModelState . SelectMany ( x = > x . Value . Errors . Select ( error = > error . ← ErrorMessage ) ) ; 153 } 154 155 # region Status Codes 156 private static string ErrorCodeToString ( MembershipCreateStatus createStatus ) 157 { 158 // See http :// go . microsoft . com / fwlink /? LinkID =177550 for 159 // a full list of status codes . 160 switch ( createStatus ) 161 { 162 case MembershipCreateStatus . DuplicateUserName : 163 return " Este nome de usuário já existe . Defina outro usuário . " ; 164 165 case MembershipCreateStatus . DuplicateEmail : www.k19.com.br 183
P ROJETO 184 166 return " Este email já foi cadastrado . Defina outro email . " ; 167 168 case MembershipCreateStatus . InvalidPassword : 169 return " Senha incorreta . " ; 170 171 case MembershipCreateStatus . InvalidEmail : 172 return " Email inválido . " ; 173 174 case MembershipCreateStatus . InvalidAnswer : 175 return " Resposta inválida para recuperar a senha . " ; 176 177 case MembershipCreateStatus . InvalidQuestion : 178 return " Questão inválida para recuperar a senha . " ; 179 180 case MembershipCreateStatus . InvalidUserName : 181 return " Usuário inválido . " ; 182 183 case MembershipCreateStatus . ProviderError : 184 return " Ocorreu um erro durante a autenticação . Se o problema persistir , ← contate o administrador . " ; 185 186 case MembershipCreateStatus . UserRejected : 187 return " O cadastro do usuário foi cancelado . Se o problema persistir , ← contate o administrador . " ; 188 189 default : 190 return " Um erro inesperado ocorreu . Se o problema persistir , contate o ← administrador . " ; 191 } 192 } 193 # endregion 194 } 195 196 } Código C# C.27: UsuarioController.cs 35 Crie uma pasta Usuario na pasta Views e acrescente os quatro arquivos abaixo. 1 @model K19CopaDoMundo . Models . ChangePasswordModel 2 3 @{ 4 ViewBag . Title = " Alteração de senha " ; 5 } 6 7 < hgroup class = " title " > 8 < h1 > @ViewBag . Title . </ h1 > 9 < h2 > Utilize este formulário para alterar a sua senha . </ h2 > 10 </ hgroup > 11 12 <p class = " message - info " > 13 Senhas devem ter no mínimo @Membership . MinRequiredPasswordLength caracteres . 14 </ p > 15 16 @using ( Html . BeginForm () ) { 17 @Html . ValidationSummary () 18 19 < fieldset > 20 < legend > Alteração de Senha </ legend > 21 < ol > 22 < li > 23 @Html . LabelFor ( m = > m . OldPassword ) 24 @Html . PasswordFor ( m = > m . OldPassword ) 25 </ li > 26 < li > 27 @Html . LabelFor ( m = > m . NewPassword ) 28 @Html . PasswordFor ( m = > m . NewPassword ) 29 </ li > 30 < li > 184 www.k19.com.br
185 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 31 @Html . LabelFor ( m = > m . ConfirmPassword ) 32 @Html . PasswordFor ( m = > m . ConfirmPassword ) 33 </ li > 34 </ ol > 35 < input type = " submit " value = " Alterar Senha " / > 36 </ fieldset > 37 } 38 39 @section Scripts { 40 @Scripts . Render ( " ~/ bundles / jqueryval " ) 41 } Código CSHTML C.10: ChangePassword.cshtml 1 @{ 2 ViewBag . Title = " Senha alterada " ; 3 } 4 5 < hgroup class = " title " > 6 < h1 > @ViewBag . Title . </ h1 > 7 < h2 > Sua senha foi alterada com sucesso . </ h2 > 8 </ hgroup > Código CSHTML C.11: ChangePasswordSuccess.cshtml 1 @model K19CopaDoMundo . Models . LoginModel 2 3 @{ 4 ViewBag . Title = " Log in " ; 5 } 6 7 < hgroup class = " title " > 8 < h1 > @ViewBag . Title . </ h1 > 9 < h2 > Formulário de Login </ h2 > 10 </ hgroup > 11 12 @using ( Html . BeginForm ( new { ReturnUrl = ViewBag . ReturnUrl }) ) { 13 @Html . ValidationSummary ( true , " Login não foi efetuado . Informe os dados corretos . " ← ) 14 15 < fieldset > 16 < legend > Formulário de Login </ legend > 17 < ol > 18 < li > 19 @Html . LabelFor ( m = > m . UserName ) 20 @Html . TextBoxFor ( m = > m . UserName ) 21 </ li > 22 < li > 23 @Html . LabelFor ( m = > m . Password ) 24 @Html . PasswordFor ( m = > m . Password ) 25 </ li > 26 < li > 27 @Html . CheckBoxFor ( m = > m . RememberMe ) 28 @Html . LabelFor ( m = > m . RememberMe , new { @class = " checkbox " }) 29 </ li > 30 </ ol > 31 < input type = " submit " value = " Log in " / > 32 </ fieldset > 33 <p > 34 @Html . ActionLink ( " Registrar " , " Register " ) . 35 </ p > 36 } 37 38 @section Scripts { 39 @Scripts . Render ( " ~/ bundles / jqueryval " ) 40 } Código CSHTML C.12: Login.cshtml www.k19.com.br 185
P ROJETO 186 1 @model K19CopaDoMundo . Models . RegisterModel 2 3 @{ 4 ViewBag . Title = " Cadastro " ; 5 } 6 7 < hgroup class = " title " > 8 < h1 > @ViewBag . Title . </ h1 > 9 < h2 > Cadastrar </ h2 > 10 </ hgroup > 11 12 <p class = " message - info " > 13 Senhas devem ter no minimo @Membership . MinRequiredPasswordLength caracteres . 14 </ p > 15 16 @using ( Html . BeginForm () ) { 17 @Html . ValidationSummary () 18 19 < fieldset > 20 < legend > Cadastro </ legend > 21 < ol > 22 < li > 23 @Html . LabelFor ( m = > m . UserName ) 24 @Html . TextBoxFor ( m = > m . UserName ) 25 </ li > 26 < li > 27 @Html . LabelFor ( m = > m . Email ) 28 @Html . TextBoxFor ( m = > m . Email ) 29 </ li > 30 < li > 31 @Html . LabelFor ( m = > m . Password ) 32 @Html . PasswordFor ( m = > m . Password ) 33 </ li > 34 < li > 35 @Html . LabelFor ( m = > m . ConfirmPassword ) 36 @Html . PasswordFor ( m = > m . ConfirmPassword ) 37 </ li > 38 </ ol > 39 < input type = " submit " value = " Cadastrar " / > 40 </ fieldset > 41 } 42 43 @section Scripts { 44 @Scripts . Render ( " ~/ bundles / jqueryval " ) 45 } Código CSHTML C.13: Register.cshtml 36 Adicione o seguinte partial View _LoginPartial.cshtml a pasta Shared. 1 @if ( Request . IsAuthenticated ) { 2 <p > 3 Olá , @Html . ActionLink ( User . Identity . Name , " ChangePassword " , " Usuario " , ← routeValues : null , htmlAttributes : new { @class = " username " , title = " ← Alterar senha " }) ! 4 @Html . ActionLink ( " Sair " , " LogOff " , " Usuario " ) 5 </ p > 6 } else { 7 < ul > 8 < li > @Html . ActionLink ( " Cadastrar " , " Register " , " Usuario " , routeValues : null , ← htmlAttributes : new { id = " registerLink " }) </ li > 9 < li > @Html . ActionLink ( " Entrar " , " Login " , " Usuario " , routeValues : null , ← htmlAttributes : new { id = " loginLink " }) </ li > 10 </ ul > 11 } Código CSHTML C.14: _LoginPartial.cshtml 186 www.k19.com.br
187 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 37 Altere o arquivo _Layout.cshtml. 1 <! DOCTYPE html > 2 < html lang = " en " > 3 < head > 4 < meta charset = " utf -8 " / > 5 < title > Copa do Mundo </ title > 6 < link href = " ~/ __TemplateIcon . ico " rel = " shortcut icon " type = " image /x - icon " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 9 @Scripts . Render ( " ~/ bundles / modernizr " ) 10 </ head > 11 < body > 12 < header > 13 < div class = " content - wrapper " > 14 < div class = " float - left " > 15 <p class = " site - title " > < img class = " logo " src = " ~/ Images / k19 - logo . jpg ← " alt = " K19 Logo " / > </ p > 16 </ div > 17 < div class = " float - right " > 18 < section id = " login " > 19 @Html . Partial ( " _LoginPartial " ) 20 </ section > 21 < nav > 22 < ul id = " menu " > 23 < li > @Html . ActionLink ( " Selecoes " , " Index " , " Selecao " , null , ← new { @class = " botao " }) </ li > 24 < li > @Html . ActionLink ( " Jogadores " , " Index " , " Jogador " , ← null , new { @class = " botao " }) </ li > 25 </ ul > 26 </ nav > 27 </ div > 28 </ div > 29 </ header > 30 < div id = " body " > 31 @RenderSection ( " featured " , required : false ) 32 < section class = " content - wrapper main - content clear - fix " > 33 @RenderBody () 34 </ section > 35 </ div > 36 < footer > 37 < div class = " content - wrapper " > 38 < div class = " float - left " > 39 <p >& copy ; @DateTime . Now . Year - K19 Copa do Mundo </ p > 40 </ div > 41 < div class = " float - right " > 42 < ul id = " social " > 43 < li > <a href = " http :// facebook . com / k19treinamentos " class = " ← facebook " > Facebook - K19 Treinamentos </ a > </ li > 44 < li > <a href = " http :// twitter . com / k19treinamentos " class = " ← twitter " > Twitter - K19 Treinamentos </ a > </ li > 45 </ ul > 46 </ div > 47 </ div > 48 </ footer > 49 @Scripts . Render ( " ~/ bundles / jquery " ) 50 @RenderSection ( " scripts " , required : false ) 51 </ body > 52 </ html > Código CSHTML C.15: _Layout.cshtml www.k19.com.br 187
P ROJETO 188 Adicionando um Usuário Administrador com ASP .NET Configuration Antes de definir o filtro Authorize nos controladores de nosso site, vamos criar um usuário com acesso. A maneira mais fácil de criar o usuário é através do ASP .NET Configuration. Exercícios de Fixação 38 Execute o ASP .NET Configuration que fica na aba “Solution Explorer” do Visual Studio. 39 Isto executará um ambiente de configuração. Abra a aba “Security” e clique no link “Enable roles”. 40 Posteriormente, clique em “Create or manage roles”. 41 Defina um role “Administrador” e clique Add Role. 42 Clique no botão “back” e crie um usuário. 43 Defina um usuário admin e senha admink19!. Autorização Role-based Podemos restringir o acesso as páginas com o filtro Authorize e podemos especificar o role que o usuário precisa ter para ter acesso a página. Exercícios de Fixação 44 Altere o filtro de autenticação no Web.config para redirecionar o usuário para a action Login do controlador Usuario. 1 < authentication mode = " Forms " > 2 < forms loginUrl = " ~/ Usuario / Login " timeout = " 2880 " / > 3 </ authentication > Código XML C.2: Web.config 45 Acrescente a seguinte string de conexão no arquivo Web.config para definir o local que as infor- mações dos usuários serão armazenadas (No nosso caso, teremos duas strings de conexão). 1 < connectionStrings > 2 < add 3 name = " K19CopaDoMundoContext " providerName = " System . Data . SqlClient " 4 connectionString = " Server =. SQLEXPRESS ; Database = k19copadomundo ; 5 User Id = sa ; Password = sa ; Trusted_Connection = False ; MultipleActiveResultSets = True " / > 6 <! -- Definindo o provedor para o Membership -- > 7 < add 8 name = " DefaultConnection " providerName = " System . Data . SqlClient " 9 connectionString = " Server =. SQLEXPRESS ; Database = k19copadomundo ; 10 User Id = sa ; Password = sa ; Trusted_Connection = False ; MultipleActiveResultSets = True " / > 11 </ connectionStrings > 188 www.k19.com.br
189 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES Código XML C.3: Web.config 46 Acrescente o filtro de autenticação nos controladores Selecoes e Jogadores através do atributo Authorize. 1 [ Authorize ( Roles = " Administrador " ) ] 2 public class SelecaoController : Controller Código C# C.28: SelecaoController.cs 1 [ Authorize ( Roles = " Administrador " ) ] 2 public class JogadorController : Controller Código C# C.29: JogadorController.cs Controle de Erro Podemos configurar uma página de erro padrão para ser utilizada toda vez que um erro ocorrer. Exercícios de Fixação 47 Acrescente ao arquivo Web.config a tag customErrors para especificar a página de erro padrão. A tag customErrors fica dentro da tag system.web. 1 < system . web > 2 ... 3 < customErrors mode = " On " defaultRedirect = " ~/ Erro / Desconhecido " > 4 < error statusCode = " 404 " redirect = " ~/ Erro / PaginaNaoEncontrada " / > 5 </ customErrors > 6 ... 7 </ system . web > Código XML C.4: Web.config 48 Defina o controlador Erro e as páginas de erros padrão. As páginas de erro padrão serão criadas dentro da pasta Views numa subpasta Erro. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web ; 5 using System . Web . Mvc ; 6 7 namespace K19CopaDoMundo . Controllers 8 { 9 public class ErroController : Controller 10 { 11 // 12 // GET : / Erro / Desconhecido 13 14 public ActionResult Desconhecido () 15 { 16 return View () ; 17 } 18 www.k19.com.br 189
P ROJETO 190 19 // 20 // GET : / Erro / PaginaNaoEncontrada 21 public ActionResult PaginaNaoEncontrada () 22 { 23 return View () ; 24 } 25 26 } 27 } Código C# C.30: ErroController.cs 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < meta name = " viewport " content = " width = device - width " / > 10 < title > Problema no servidor </ title > 11 </ head > 12 < body > 13 < h2 > Desculpe , tivemos problema em nosso servidor . Volte dentro de alguns instantes ← . </ h2 > 14 </ body > 15 </ html > Código CSHTML C.16: Desconhecido.cshtml 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < meta name = " viewport " content = " width = device - width " / > 10 < title > Página não encontrada </ title > 11 </ head > 12 < body > 13 < h2 > Página não encontrada </ h2 > 14 </ body > 15 </ html > Código CSHTML C.17: PaginaNaoEncontrada.cshtml Enviando email Quando um erro ocorre na nossa aplicação, podemos permitir que o usuário envie uma email para os administradores do sistema. Para enviar as mensagens, podemos utilizar o Web Exercícios de Fixação 49 Altere a tela de erro adicionando um formulário para o usuário escrever uma mensagem para os administradores da aplicação. 1 @{ 190 www.k19.com.br
191 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < meta name = " viewport " content = " width = device - width " / > 10 < title > Problema no servidor </ title > 11 </ head > 12 < body > 13 < h2 > Desculpe , tivemos problema em nosso servidor . Volte dentro de alguns instantes ← . </ h2 > 14 <p > Envie uma mensagem para os administradores do sistema . </ p > 15 @using ( Html . BeginForm ( " Envia " , " Email " ) ) 16 { 17 < div class = " editor - label " > 18 @Html . Label ( " Mensagem " ) 19 </ div > 20 < div class = " editor - field " > 21 @Html . TextArea ( " Mensagem " ) 22 </ div > 23 < input type = " submit " value = " Enviar " / > 24 } 25 </ body > 26 </ html > Código CSHTML C.18: Desconhecido.cshtml 50 Crie um controlador que envie as mensagens por email utilizando o helper WebMail. Observa- ção, utilize usuários, senhas e emails válidos do gmail para este exercício. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web ; 5 using System . Web . Mvc ; 6 using System . Web . Helpers ; 7 8 namespace K19CopaDoMundo . Controllers 9 { 10 public class EmailController : Controller 11 { 12 13 public EmailController () 14 { 15 WebMail . SmtpServer = " smtp . gmail . com " ; 16 WebMail . EnableSsl = true ; 17 WebMail . SmtpPort = 587; 18 WebMail . From = " USUARIO@gmail . com " ; 19 WebMail . UserName = " USUARIO@gmail . com " ; 20 WebMail . Password = " SENHA " ; 21 } 22 // 23 // POST : / Email / Envia 24 [ HttpPost ] 25 public ActionResult Envia ( string mensagem ) 26 { 27 28 WebMail . Send ( " EMAIL " , " Copa do Mundo - Erro " , mensagem ) ; 29 return View () ; 30 } 31 32 } 33 } Código C# C.31: EmailController.cs www.k19.com.br 191
P ROJETO 192 51 Crie uma página Envia.cshtml para mostrar ao usuário que a mensagem foi enviada com su- cesso e acrescente um link para a página inicial do site. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < title > Envia </ title > 10 </ head > 11 < body > 12 < div > 13 Mensagem enviada com sucesso . 14 </ div > 15 < div > 16 @Html . ActionLink ( " Voltar para página inicial " , " Index " , " Selecoes " ) 17 </ div > 18 </ body > 19 </ html > Código CSHTML C.19: Envia.cshtml 192 www.k19.com.br
APÊNDICE R ESPOSTAS D Resposta do Complementar 2.1 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereLivro 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Título do Livro : " ) ; 13 string titulo = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Preço do Livro : " ) ; 16 string preco = System . Console . ReadLine () ; 17 18 System . Console . Write ( " Digite o Id da Editora do Livro : " ) ; 19 string editoraId = System . Console . ReadLine () ; 20 21 string textoInsereEditora = 22 @ " INSERT INTO Livros ( Titulo , Preco , EditoraId ) 23 VALUES ( ’ " + titulo + @ " ’, ’" + preco + @ " ’, " + editoraId + " ) " ; 24 25 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 26 { 27 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 28 conexao . Open () ; 29 command . ExecuteNonQuery () ; 30 } 31 } 32 } 33 } Código C# 2.9: InsereLivro.cs Resposta do Complementar 2.3 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereLivro 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Título do Livro : " ) ; www.k19.com.br 193
R ESPOSTAS 194 13 string titulo = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Preço do Livro : " ) ; 16 string preco = System . Console . ReadLine () ; 17 18 System . Console . Write ( " Digite o Id da Editora do Livro : " ) ; 19 string editoraId = System . Console . ReadLine () ; 20 21 string textoInsereEditora = 22 @ " INSERT INTO Livros ( Titulo , Preco , EditoraId ) VALUES (? ,? ,?) " ; 23 24 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 25 { 26 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 27 28 command . Parameters . AddWithValue ( " @Titulo " , titulo ) ; 29 command . Parameters . AddWithValue ( " @Preco " , preco ) ; 30 command . Parameters . AddWithValue ( " @EditoraId " , editoraId ) ; 31 32 conexao . Open () ; 33 command . ExecuteNonQuery () ; 34 } 35 } 36 } 37 } Código C# 2.14: InsereLivro.cs Resposta do Complementar 2.5 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaLivro 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 13 { 14 string textoListaEditora = " SELECT * FROM Livros " ; 15 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 16 conexao . Open () ; 17 OdbcDataReader resultado = command . ExecuteReader () ; 18 19 while ( resultado . Read () ) 20 { 21 long ? id = resultado [ " Id " ] as long ?; 22 string titulo = resultado [ " Titulo " ] as string ; 23 double ? preco = resultado [ " Preco " ] as double ?; 24 long ? editoraId = resultado [ " EditoraId " ] as long ?; 25 26 System . Console . WriteLine ( " {0} : {1} - {2} - {3} n " , 27 id , titulo , preco , editoraId ) ; 28 } 29 } 30 } 31 } 32 } Código C# 2.21: ListaLivro.cs 194 www.k19.com.br
195 R ESPOSTAS Resposta do Complementar 2.6 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereLivro 6 { 7 static void Main ( string [] args ) 8 { 9 System . Console . Write ( " Digite o Título do Livro : " ) ; 10 string titulo = System . Console . ReadLine () ; 11 12 System . Console . Write ( " Digite o Preço do Livro : " ) ; 13 string preco = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Id da Editora do Livro : " ) ; 16 string editoraId = System . Console . ReadLine () ; 17 18 string textoInsereEditora = 19 @ " INSERT INTO Livros ( Titulo , Preco , EditoraId ) VALUES (? ,? ,?) " ; 20 21 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 22 { 23 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 24 25 command . Parameters . AddWithValue ( " @Titulo " , titulo ) ; 26 command . Parameters . AddWithValue ( " @Preco " , preco ) ; 27 command . Parameters . AddWithValue ( " @EditoraId " , editoraId ) ; 28 29 conexao . Open () ; 30 command . ExecuteNonQuery () ; 31 } 32 } 33 } 34 } Código C# 2.26: InsereLivro.cs 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaLivro 6 { 7 static void Main ( string [] args ) 8 { 9 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 10 { 11 string textoListaEditora = " SELECT * FROM Livros " ; 12 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 13 conexao . Open () ; 14 OdbcDataReader resultado = command . ExecuteReader () ; 15 16 while ( resultado . Read () ) 17 { 18 long ? id = resultado [ " Id " ] as long ?; 19 string titulo = resultado [ " Titulo " ] as string ; 20 double ? preco = resultado [ " Preco " ] as double ?; 21 long ? editoraId = resultado [ " EditoraId " ] as long ?; 22 23 System . Console . WriteLine ( " {0} : {1} - {2} - {3} n " , 24 id , titulo , preco , editoraId ) ; 25 } 26 } www.k19.com.br 195
R ESPOSTAS 196 27 } 28 } 29 } Código C# 2.27: ListaLivro.cs Resposta do Complementar 5.1 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 5 @for ( int i = 0; i < 10; i ++) 6 { 7 < h2 > Olá @i </ h2 > 8 } Código CSHTML 5.10: Home.cshtml 196 www.k19.com.br

K19 k32-desenvolvimento-web-com-aspnet-mvc

  • 1.
  • 3.
    Desenvolvimento Web comASP.NET MVC 4 25 de junho de 2012 Sumário i Sobre a K19 1 Seguro Treinamento 2 Termo de Uso 3 Cursos 4 1 Banco de Dados 1 1.1 Sistemas Gerenciadores de Banco de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 SQL Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Bases de Dados (Databases) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.4 Tabelas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.5 CRUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.6 Chaves Primária e Estrangeira . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.7 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2 ADO.NET 23 2.1 Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2 ODBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3 ODBC Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.4 Criando uma conexão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.5 Inserindo registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.7 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.8 SQL Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.10 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.11 Listando registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.12 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.13 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.14 Connection Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 www.k19.com.br i
  • 4.
    S UMÁRIO ii 2.15 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.16 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.17 Desafios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3 Entity Framework 37 3.1 Múltiplas sintaxes da linguagem SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.2 Orientação a Objetos VS Modelo Relacional . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.3 Ferramentas ORM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.4 Configuração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.5 Mapeamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.6 Manipulando entidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.7 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.8 Repositórios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4 Visão Geral do ASP.NET MVC 53 4.1 Necessidades de uma aplicação web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.2 ASP .NET MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.3 MVC e Front Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.4 Visual Web Developer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.5 Exemplo de uma Aplicação Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.7 Integração com Entity Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.8 Scaffold . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5 Camada de Apresentação 67 5.1 Razor e ASPX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 5.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.3 Exercícios Complementares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.4 ViewBag e Strogly Typed Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.5 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.6 HTML Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 5.7 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.8 Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.10 Partial views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 5.11 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 6 Camada de Controle 99 6.1 Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 6.2 ActionResult . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 6.3 Parâmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 6.5 TempData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 6.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 7 Rotas 107 7.1 Adicionando uma rota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 7.2 Adicionando Parâmetros nas Rotas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 ii www.k19.com.br
  • 5.
    iii S UMÁRIO 7.3 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 8 Validação 111 8.1 Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 8.2 View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 8.3 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 8.4 Anotações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 8.5 Validação no lado do Cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 8.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 9 Sessão 119 9.1 Identificando os navegadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 9.2 Sessões no ASP .NET MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 9.3 Session Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 9.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 10 Autenticação 125 10.1 Filtro de Autenticação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 10.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 11 Tratamento de Erros 131 11.1 Try-Catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 11.2 Custom Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 11.3 Http Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 11.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 A ASP.NET Web API 139 A.1 REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 A.2 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 A.3 URIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 A.4 Operações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 A.5 Media Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 A.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 B Migrations 147 B.1 Passo a Passo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 B.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 C Projeto 161 C.1 Modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 C.2 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 C.3 Persistência - Mapeamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 C.4 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 C.5 Persistência - Configuração . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.6 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.7 Persistência - Repositórios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.8 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 C.9 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 C.10 Apresentação - Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 C.11 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 C.12 Cadastrando e Listando Seleções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 www.k19.com.br iii
  • 6.
    S UMÁRIO iv C.13 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 C.14 Removendo Seleções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 C.15 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 C.16 Cadastrando, Listando e Removendo Jogadores . . . . . . . . . . . . . . . . . . . . . . . 174 C.17 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 C.18 Removendo Jogadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 C.19 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 C.20 Membership e Autorização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 C.21 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 C.22 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 C.23 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 C.24 Controle de Erro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 C.25 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 C.26 Enviando email . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 C.27 Exercícios de Fixação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 D Respostas 193 iv www.k19.com.br
  • 7.
    1 S UMÁRIO Sobre a K19 A K19 é uma empresa especializada na capacitação de desenvolvedores de software. Sua equipe é composta por profissionais formados em Ciência da Computação pela Universidade de São Paulo (USP) e que possuem vasta experiência em treinamento de profissionais para área de TI. O principal objetivo da K19 é oferecer treinamentos de máxima qualidade e relacionados às prin- cipais tecnologias utilizadas pelas empresas. Através desses treinamentos, seus alunos se tornam capacitados para atuar no mercado de trabalho. Visando a máxima qualidade, a K19 mantém as suas apostilas em constante renovação e melho- ria, oferece instalações físicas apropriadas para o ensino e seus instrutores estão sempre atualizados didática e tecnicamente. www.k19.com.br 1
  • 8.
    S UMÁRIO 2 Seguro Treinamento Na K19 o aluno faz o curso quantas vezes quiser! Comprometida com o aprendizado e com a satisfação dos seus alunos, a K19 é a única que pos- sui o Seguro Treinamento. Ao contratar um curso, o aluno poderá refazê-lo quantas vezes desejar mediante a disponibilidade de vagas e pagamento da franquia do Seguro Treinamento. As vagas não preenchidas até um dia antes do início de uma turma da K19 serão destinadas ao alunos que desejam utilizar o Seguro Treinamento. O valor da franquia para utilizar o Seguro Treina- mento é 10% do valor total do curso. 2 www.k19.com.br
  • 9.
    3 S UMÁRIO Termo de Uso Termo de Uso Todo o conteúdo desta apostila é propriedade da K19 Treinamentos. A apostila pode ser utilizada livremente para estudo pessoal . Além disso, este material didático pode ser utilizado como material de apoio em cursos de ensino superior desde que a instituição correspondente seja reconhecida pelo MEC (Ministério da Educação) e que a K19 seja citada explicitamente como proprietária do material. É proibida qualquer utilização desse material que não se enquadre nas condições acima sem o prévio consentimento formal, por escrito, da K19 Treinamentos. O uso indevido está sujeito às medidas legais cabíveis. www.k19.com.br 3
  • 10.
    S UMÁRIO 4 TR EIN AM EN TR TO EIN S TREINAMENTOS AM EN TO S Conheça os nossos cursos K01- Lógica de Programação K11 - Orientação a Objetos em Java K12 - Desenvolvimento Web com JSF2 e JPA2 K21 - Persistência com JPA2 e Hibernate K22 - Desenvolvimento Web Avançado com JFS2, EJB3.1 e CDI K23 - Integração de Sistemas com Webservices, JMS e EJB K31 - C# e Orientação a Objetos K32 - Desenvolvimento Web com ASP.NET MVC www.k19.com.br/cursos 4 www.k19.com.br
  • 11.
    CAPÍTULO BANCO DE D ADOS 1 Em geral, as aplicações necessitam armazenar dados de forma persistente para consultá-los pos- teriormente. Por exemplo, a aplicação de uma livraria precisa armazenar os dados dos livros e dos autores de forma persistente. Suponha que esses dados sejam armazenados em arquivos do sistema operacional. Vários fato- res importantes nos levam a descartar tal opção. A seguir, apresentamos as principais dificuldades a serem consideradas na persistência de dados. Segurança: O acesso às informações potencialmente confidenciais deve ser controlado de forma que apenas usuários e sistemas autorizados possam manipulá-las. Integridade: Restrições relacionadas aos dados armazenados devem ser respeitadas para que as in- formações estejam sempre consistentes. Consulta: O tempo gasto para realizar as consultas aos dados armazenados deve ser o menor possí- vel. Concorrência: Em geral, diversos sistemas e usuários acessarão concorrentemente as informações armazenadas. Apesar disso, a integridade dos dados deve ser preservada. Considerando todos esses aspectos, concluímos que um sistema complexo seria necessário para persistir as informações de uma aplicação de maneira adequada. Felizmente, tal tipo de sistema já existe e é conhecido como Sistema Gerenciador de Banco de Dados (SGBD). Figura 1.1: Sistema Gerenciador de Banco de Dados Sistemas Gerenciadores de Banco de Dados No mercado, há diversas opções de sistemas gerenciadores de banco de dados. Os mais populares são: • Oracle Database • SQL Server www.k19.com.br 1
  • 12.
    B ANCO DED ADOS 2 • MySQL Server • PostgreSQL SQL Server Neste treinamento, utilizaremos o SQL Server Express, que é mantido pela Microsoft. O SQL Server Express pode ser obtido a partir do site: http://www.microsoft.com/express/Database/. Microsoft SQL Server Management Studio Express Para interagir com o SQL Server Express, utilizaremos um cliente com interface gráfica chamado de Microsoft SQL Server Management Studio Express. Bases de Dados (Databases) Um sistema gerenciador de banco de dados é capaz de gerenciar informações de diversos sistemas ao mesmo tempo. Por exemplo, as informações dos clientes de um banco, além dos produtos de uma loja virtual ou dos livros de uma livraria. Suponha que os dados fossem mantidos sem nenhuma separação lógica. Implementar regras de segurança específicas seria extremamente complexo. Tais regras criam restrições quanto ao con- teúdo que pode ser acessado por cada usuário. Por exemplo, determinado usuário poderia ter per- missão de acesso aos dados dos clientes do banco, mas não às informações dos produtos da loja virtual, ou dos livros da livraria. Para obter uma organização melhor, os dados são armazenados separadamente em um SGDB. Daí surge o conceito de base de dados (database). Uma base de dados é um agrupamento lógico das informações de um determinado domínio. Criando uma base de dados no SQL Server Express Para criar uma base de dados no SQL Server Express, utilizamos o comando CREATE DATABASE. 2 www.k19.com.br
  • 13.
    3 B ANCO DE D ADOS www.k19.com.br 3
  • 14.
    B ANCO DED ADOS 4 Repare que além da base de dados livraria há outras bases. Essas bases foram criadas automati- camente pelo próprio SQL Server Express para teste ou para guardar algumas configurações. Quando uma base de dados não é mais necessária, ela pode ser removida através do comando DROP DATABASE. Tabelas Um servidor de banco de dados é dividido em bases de dados com o intuito de separar as informa- ções de domínios diferentes. Nessa mesma linha de raciocínio, podemos dividir os dados de uma base a fim de agrupá-los segundo as suas correlações. Essa separação é feita através de tabelas. Por exemplo, no sistema de um banco, é interessante separar o saldo e o limite de uma conta, do nome e CPF de um cliente. Então, poderíamos criar uma tabela para os dados relacionados às contas e outra para os dados relacionados aos clientes. Cliente Conta nome idade cpf numero saldo limite José 27 31875638735 1 1000 500 Maria 32 30045667856 2 2000 700 Tabela 1.1: Tabelas para armazenar os dados relacionados aos clientes e às contas Uma tabela é formada por registros (linhas) e os registros são formados por campos (colunas). Por exemplo, considere uma tabela para armazenar as informações dos clientes de um banco. Cada registro dessa tabela armazena em seus campos os dados de um determinado cliente. Criando tabelas no SQL Server Express As tabelas no SQL Server Express são criadas através do comando CREATE TABLE. Na criação de uma tabela é necessário definir quais são os nomes e os tipos das colunas. 4 www.k19.com.br
  • 15.
    5 B ANCO DE D ADOS www.k19.com.br 5
  • 16.
    B ANCO DED ADOS 6 No SQL Server os nomes das tabelas são precedidas pelo ID do usuário que possui a tabela. No caso do usuário sa, o ID é dbo. Portanto o nome da tabela Livros fica dbo.Livros. Se uma tabela não for mais desejada ela pode ser removida através do comando DROP TABLE. CRUD As operações básicas para manipular os dados persistidos são: inserir, ler, alterar e remover. Essas operações são realizadas através de uma linguagem de consulta denominada SQL (Structu- red Query Language). Essa linguagem oferece quatro comandos básicos: INSERT, SELECT, UPDATE 6 www.k19.com.br
  • 17.
    7 B ANCO DE D ADOS e DELETE. Esses comandos são utilizados para inserir, ler, alterar e remover registros, respectiva- mente. www.k19.com.br 7
  • 18.
    B ANCO DED ADOS 8 8 www.k19.com.br
  • 19.
    9 B ANCO DE D ADOS Chaves Primária e Estrangeira Suponha que os livros da nossa livraria sejam classificados por editoras. As editoras possuem nome e telefone. Para armazenar esses dados, uma nova tabela deveria ser criada. Nesse momento, teríamos duas tabelas (Livro e Editora). Constantemente, a aplicação da livraria deverá descobrir qual é a editora de um determinado livro ou quais são os livros de uma determinada editora. Para isso, os registros da tabela Editora devem estar relacionados aos da tabela Livro. Na tabela Livro, poderíamos adicionar uma coluna para armazenar o nome da editora dos livros. Dessa forma, se alguém quiser recuperar as informações da editora de um determinado livro, deve www.k19.com.br 9
  • 20.
    B ANCO DED ADOS 10 consultar a tabela Livro para obter o nome da editora correspondente. Depois, com esse nome, deve consultar a tabela Editora para obter as informações da editora. Porém, há um problema nessa abordagem. A tabela Editora aceita duas editoras com o mesmo nome. Dessa forma, eventualmente, não conseguiríamos descobrir os dados corretos da editora de um determinado livro. Para resolver esse problema, deveríamos criar uma restrição na tabela Editora que proíba a inserção de editoras com o mesmo nome. Para resolver esse problema no SQL Server Express, poderíamos adicionar a propriedade UNI- QUE no campo nome da tabela Editora. Porém, ainda teríamos mais um problema. Na tabela Li- vro, poderíamos adicionar registros vinculados a editoras inexistentes, pois não há nenhuma relação explícita entre as tabelas. Para solucionar esses problemas, devemos utilizar o conceito de chave primária e chave estrangeira. Toda tabela pode ter uma chave primária, que é um conjunto de um ou mais campos que de- vem ser únicos para cada registro. Normalmente, um campo numérico é escolhido para ser a chave primária de uma tabela, pois as consultas podem ser realizadas com melhor desempenho. Então, poderíamos adicionar um campo numérico na tabela Editora e torná-lo chave primária. Vamos chamar esse campo de id. Na tabela Livro, podemos adicionar um campo numérico chamado editora_id que deve ser utilizado para guardar o valor da chave primária da editora correspondente ao livro. Além disso, o campo editora_id deve estar explicitamente vinculado com o campo id da tabela Editora. Para estabelecer esse vínculo, o campo editora_id da tabela Livro deve ser uma chave estrangeira associada à chave primária da tabela Editora. Uma chave estrangeira é um conjunto de uma ou mais colunas de uma tabela que possuem va- lores iguais aos da chave primária de outra tabela. Com a definição da chave estrangeira, um livro não pode ser inserido com o valor do campo editora_id inválido. Caso tentássemos fazer isso, obteríamos uma mensagem de erro. Exercícios de Fixação 1 Abra o Microsoft SQL Server Management Studio Express utilizando NOME_DA_MAQUINA SQLEXPRESS como Server Name, SQL Server Authentication como Authentication, sa como Login e sa como Password. 10 www.k19.com.br
  • 21.
    11 B ANCO DE D ADOS 2 Caso exista uma base de dados chamada Livraria, remova-a conforme a figura abaixo: 3 Crie uma nova base de dados chamada livraria, conforme mostrado na figura abaixo. Você vai utilizar esta base nos exercícios seguintes. www.k19.com.br 11
  • 22.
    B ANCO DED ADOS 12 4 Crie uma tabela chamada Editoras conforme as figuras abaixo. 12 www.k19.com.br
  • 23.
    13 B ANCO DE D ADOS Altere os campos para torná-los obrigatórios, NÃO permitindo que eles fiquem em branco NU LL. Além disso o campo Id deve ser uma chave primária. www.k19.com.br 13
  • 24.
    B ANCO DED ADOS 14 O campo Id dever ser incrementado automaticamente. Defina ele com a propriedade Identity segundo a figura abaixo: 14 www.k19.com.br
  • 25.
    15 B ANCO DE D ADOS 5 Crie uma tabela chamada Livros conforme as figuras abaixo: Lembrando de NÃO marcar a opção ALLOW NULL. Além disso o campo Id deve ser uma chave primária e automaticamente incrementada. Você precisa tornar o campo EditoraId uma chave estrangeira. Clique com o botão direito sobre a coluna EditoraId e selecione a opção Relantioships..., conforme a figura abaixo: www.k19.com.br 15
  • 26.
    B ANCO DED ADOS 16 Devemos acrescentar o relacionamento entre livro e editora. Clique em Add e posteriormente no botão à direita na linha Tables and Columns Specification. Devemos informar qual é a chave primária que a coluna EditoraId da tabela Livros faz referência. 16 www.k19.com.br
  • 27.
    17 B ANCO DE D ADOS Para isto, informe a tabela Editoras como Primary Key Table e indique a coluna Id como a chave primária referenciada. Selecione a coluna EditoraId como a coluna que irá fazer referência a chave primária da tabela Editoras. 6 Adicione alguns registros na tabela Editoras. Veja exemplos na figura abaixo: Adicione alguns registros na tabela Livros. Veja exemplos na figura abaixo: www.k19.com.br 17
  • 28.
    B ANCO DED ADOS 18 7 Consulte os registros da tabela Editoras, e em seguida consulte a tabela Livros. Veja exemplos logo abaixo: 18 www.k19.com.br
  • 29.
    19 B ANCO DE D ADOS 8 Altere alguns dos registros da tabela Livros. Veja o exemplo abaixo: 9 Altere alguns dos registros da tabela Editoras. Veja o exemplo abaixo: www.k19.com.br 19
  • 30.
    B ANCO DED ADOS 20 10 Remova alguns registros da tabela Livros. Veja o exemplo abaixo: 11 Remova alguns registros da tabela Editoras. Preste atenção para não remover uma editora que tenha algum livro relacionado já adicionado no banco. Veja o exemplo abaixo: 20 www.k19.com.br
  • 31.
    21 B ANCO DE D ADOS 12 Faça uma consulta para buscar todos os livros de uma determinada editora. Veja um exemplo na figura abaixo: www.k19.com.br 21
  • 32.
    B ANCO DED ADOS 22 22 www.k19.com.br
  • 33.
    CAPÍTULO ADO.NET 2 No capítulo anterior, aprendemos que utilizar bancos de dados é uma ótima alternativa para armazenar os dados de uma aplicação. Entretanto, você deve ter percebido que as interfaces dispo- níveis para interagir com o SQL Server Express não podem ser utilizadas por qualquer pessoa. Para utilizá-las, é necessário conhecer a linguagem SQL e os conceitos do modelo relacional. Em geral, as interfaces dos outros SGDBs exigem os mesmos conhecimentos. SELECT * FROM tbl_funcionarios WHERE nome LIKE ‘%jonas%’; INSERT INTO tbl_funcionarios (nome, codigo, salario) VALUES (’Rafael’, 1234, 1000); Figura 2.1: Usuários comuns não possuem conhecimento sobre SQL ou sobre o modelo relacional Para resolver esse problema, podemos desenvolver aplicações com interfaces que não exijam co- nhecimentos técnicos de SQL ou do modelo relacional para serem utilizadas. Dessa forma, usuários comuns poderiam manipular as informações do banco de dados através dessas aplicações. Nessa abordagem, os usuários interagem com as aplicações e as aplicações interagem com os SGDBs. www.k19.com.br Cadastro de Funcionários Nome: Código: Salário: Figura 2.2: Usuários comuns devem utilizar interfaces simples www.k19.com.br 23
  • 34.
    ADO.NET 24 Driver As aplicações interagem com os SGDBs através de troca de mensagens. Os SGDBs definem o formato das mensagens. Para não sobrecarregar o canal de comunicação entre as aplicações e os SGDBs, as mensagens trocadas devem ocupar o menor espaço possível. Geralmente, protocolos binários são mais apropriados para reduzir o tamanho das mensagens e consequentemente diminuir a carga do canal de comunicação. Por isso, os SGDBs utilizam protocolos binários. rollback find getReference persist begin getTransaction commit 10110 111000 10010 Figura 2.3: Diminuindo o tamanho das mensagens para não sobrecarregar o meio de comunicação Mensagens binárias são facilmente interpretadas por computadores. Por outro lado, são com- plexas para um ser humano compreender. Dessa forma, o trabalho dos desenvolvedores seria muito complexo, aumentando o custo para o desenvolvimento e manutenção das aplicações. 10 010 11 110 01 1011010111 11 11 11 0 0010110011 0 0010110001 01 00 01 1101011101 10 111 10 110 11 01 01 1010111101 10 01 10 1 0101101001 0 01 01 00 11 0111011100 0111011100 01 101 11 0101101001 1010111101 1 010 1101011101 0010110001 0 0010110011 1011010111 Figura 2.4: Mensagens binárias são altamente complexas para os seres humanos Para resolver esse problema e facilitar o desenvolvimento das aplicações, as empresas proprietá- 24 www.k19.com.br
  • 35.
    25 ADO.NET rias dos SGDBs, normalmente, desenvolvem e distribuem drivers de conexão. Um driver de conexão atua como um intermediário entre as aplicações e os SGDBs. Os drivers de conexão são “tradutores” de comandos escritos em uma determinada linguagem de programação para comandos definidos de acordo com o protocolo de um SGDB. Utilizando um driver de conexão, os desenvolvedores das aplicações não manipulam diretamente as mensagens binárias trocadas entre as aplicações e os SGDBs. Mais Sobre Em alguns casos, o protocolo binário de um determinado SGDB é fechado. Consequen- temente, a única maneira de se comunicar com ele é através de um driver de conexão oferecido pelo fabricante desse SGDB. ODBC Suponha que os proprietários dos bancos de dados desenvolvessem os drivers de maneira total- mente independente. Consequentemente, cada driver teria sua própria interface, ou seja, seu pró- prio conjunto de instruções. Dessa maneira, o desenvolvedor da aplicação precisa conhecer as ins- truções de cada um dos drivers dos respectivos bancos que ele for utilizar. Para facilitar o trabalho do desenvolvedor da aplicação, a Microsoft® definiu uma especificação chamada ODBC (Open Database Connectivity) para padronizar a interface dos drivers de conexão. Assim, quando uma empresa proprietária de um banco de dados pretende desenvolver um driver, ela segue essa especificação com o intuito de popularizá-lo. Os drivers de conexão que respeitam a especificação ODBC, ou seja, possuem um conjunto de comandos padronizados, são chamados de drivers de conexão ODBC. ODBC Manager Para que drivers ODBC possam ser instalados em uma máquina e as aplicações consigam utilizá-los é necessário ter o ODBC Manager, que já vem instalado no Windows®. O driver de conexão ODBC já está disponível para utilização, podemos consultar o ODBC Mana- ger do Windows®. O ODBC Manager pode ser executado através do item Ferramentas Administrati- vas do Painel de Controle. www.k19.com.br 25
  • 36.
    ADO.NET 26 Criando uma conexão Com o driver de conexão ODBC instalado na máquina já é possível criar uma conexão com o banco de dados correspondente. O que é necessário para estabelecer uma conexão com o banco de dados? • Escolher o driver de conexão; • Definir a localização do banco de dados; • Informar o nome da base de dados; • Ter um usuário e senha cadastrados no banco de dados. Todas essas informações são definidas na chamada string de conexão. 1 string stringDeConexao = @ " driver ={ SQL Server }; 2 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; Código C# 2.1: Definindo a string de conexão Após a definição da string de conexão, podemos utilizar a classe System.Data.Odbc.OdbcConnec- tion do .NET Framework. Essa classe é responsável por criar conexões ODBC. 1 OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ; Código C# 2.2: Criando uma conexão ODBC Inserindo registros Estabelecida a conexão com o banco de dados, já podemos executar comandos. Por exemplo, é pos- sível inserir registros nas tabelas. O primeiro passo para executar um comando é defini-lo em lin- guagem SQL de acordo com a sintaxe do SGDB utilizado. 26 www.k19.com.br
  • 37.
    27 ADO.NET 1 string textoDoComando = @ " INSERT INTO Editoras ( Nome , Email ) 2 VALUES ( ’ Abril ’ , ’ abril@email . com ’) ; " ; Mais Sobre O caractere “@” antes de um valor literal do tipo string indica que os caracteres dentro da string não devem ser processados como caracteres especiais. Em seguida, devemos criar um objeto da classe System. Data.Odbc.OdbcCommand a partir do código sql e da conexão previamente criados. O comando não é executado quando os objetos dessa classe são instanciados. 1 OdbcCommand comando = new OdbcCommand ( textoDoComando , conexao ) ; Código C# 2.4: Criando um comando ODBC Por fim, o comando pode ser executado através do método ExecuteNonQuery(). A conexão deve ser aberta antes de executar o comando. 1 conexao . Open () ; 2 comando . ExecuteNonQuery () ; Importante A mesma conexão pode ser reaproveitada para executar várias operações. Quando não houver mais operações a serem executadas, devemos finalizar a conexão ODBC através do método Close(). Finalizar as conexões ODBC que não são mais necessárias é importante pois libera recursos no SGBD. 1 conexao . close () ; Código C# 2.6: Finalizando uma conexão ODBC Mais Sobre Em C#, para evitar que o uma conexão não seja fechada após a sua utilização, podemos aplicar um bloco using. 1 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 2 { 3 // utiliza a conexao 4 } Código C# 2.7: Utilizando um bloco using No código acima, quando o bloco using que está associado à conexão ODBC terminar, automa- ticamente, essa conexão será fechada. Exercícios de Fixação www.k19.com.br 27
  • 38.
    ADO.NET 28 1 Crie um projeto do tipo Console Application no Microsoft Visual C# Express, chamado ODBC. 2 Crie uma classe chamada InsereEditora no projeto ODBC para inserir registros na tabela Edi- toras. 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereEditora 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Nome da Editora : " ) ; 13 string nome = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Email da Editora : " ) ; 16 string email = System . Console . ReadLine () ; 17 18 string textoInsereEditora = 19 @ " INSERT INTO Editoras ( Nome , Email ) 20 VALUES ( ’ " + nome + @ " ’, ’" + email + @ " ’) " ; 21 22 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 23 { 24 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 25 conexao . Open () ; 26 command . ExecuteNonQuery () ; 27 } 28 } 29 } 30 } Código C# 2.8: InsereEditora.cs Exercícios Complementares 1 Crie uma classe chamada InsereLivro no projeto ODBC para inserir registros na tabela Livros. SQL Injection A implementação da inserção de registros feita anteriormente possui uma falha grave. Os dados ob- tidos do usuário através do teclado não são tratados antes de serem enviados para o SGDB. Esses dados podem conter caracteres especiais. Se esses caracteres não são tratados, o compor- tamento esperado da operação é afetado. Eventualmente, registros não são inseridos como deveriam ou brechas de segurança podem se abrir. Por exemplo, considere a classe InsereEditora do exercício de fixação. Se o usuário digitar “O’Reilly” e “oreilly@email.com”, o código SQL gerado pela aplicação seria: 1 INSERT INTO Editoras ( nome , email ) VALUES ( ’O ’ Reilly ’ , ’ oreilly@email . com ’) 28 www.k19.com.br
  • 39.
    29 ADO.NET Observe que o caractere aspas simples aparece cinco vezes no código SQL acima. O SGDB não saberia dizer onde de fato termina o nome da editora. Ao tentar executar esse código, um erro de sintaxe é lançado pelo SQL Server. Para resolver esse problema manualmente, devemos adicionar dois caracteres “”’ seguidos. 1 INSERT INTO Editoras ( nome , email ) VALUES ( ’O ’ ’ Reilly ’ , ’ oreilly@email . com ’) Os valores recebidos dos usuários devem ser analisados e os caracteres especiais contidos nesses valores devem ser tratados. Esse processo é extremamente trabalhoso, pois o conjunto de caracteres especiais e a forma de tratá-los é diferente em cada SGDB. A responsabilidade do tratamento dos caracteres especiais contidos nos valores de entrada dos usuários pode ser repassada para os drivers ODBC. Dessa forma, o código das aplicações se torna independente das particularidades desse processo para cada SGDB. Mais Sobre O processo de tratamento dos caracteres especiais das entradas dos usuários é denomi- nado sanitize. 1 // pegando os dados da editora pelo teclado 2 string nome = System . Console . ReadLine () ; 3 string email = System . Console . ReadLine () ; 4 5 // definindo a sentença SQL com parâmetros 6 string textoDoComando = 7 @ " INSERT INTO Editoras ( Nome , Email ) VALUES (? , ?) ; " ; 8 9 // criando um comando odbc 10 OdbcCommand comando = new OdbcCommand ( textoDoComando , conexao ) ; 11 12 // atribuindo valores aos parâmetros 13 comando . Parameters . AddWithValue ( " @Nome " , nome ) ; 14 comando . Parameters . AddWithValue ( " @Email " , email ) ; Código C# 2.12: “Sanitizando” as entradas dos usuários Observe que a sentença SQL foi definida com parâmetros através do caractere “?”. Antes de exe- cutar o comando, é necessário atribuir valores aos parâmetros. Isso é feito com o método AddWith- Value(). Esse método realiza a tarefa de “sanitizar” os valores enviados pelo usuário. Exercícios de Fixação 3 Tente causar um erro de SQL Injection ao inserir editoras com a classe InsereEditora. (Dica: tente entradas com aspas simples) 4 Altere o código da classe InsereEditora para eliminar o problema do SQL Injection. Observe o código abaixo: 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereEditora www.k19.com.br 29
  • 40.
    ADO.NET 30 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Nome da Editora : " ) ; 13 string nome = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Email da Editora : " ) ; 16 string email = System . Console . ReadLine () ; 17 18 string textoInsereEditora = 19 @ " INSERT INTO Editoras ( Nome , Email ) VALUES (? ,?) " ; 20 21 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 22 { 23 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 24 25 command . Parameters . AddWithValue ( " @Nome " , nome ) ; 26 command . Parameters . AddWithValue ( " @Email " , email ) ; 27 28 conexao . Open () ; 29 command . ExecuteNonQuery () ; 30 } 31 } 32 } 33 } Código C# 2.13: InsereEditora.cs 5 Agora tente causar novamente o problema de SQL Injection ao inserir novas editoras. Exercícios Complementares 2 Provoque um erro de SQL Injection na classe InsereLivro. (Dica: tente entradas com aspas simples.) 3 Altere o código para eliminar o problema do SQL Injection. 4 Agora tente causar novamente o problema de SQL Injection ao inserir novos livros. Listando registros Depois de inserir alguns registros, é interessante consultar os dados das tabelas para conferir se a in- serção foi realizada com sucesso. O processo para executar um comando de consulta é parecido com o de inserção. É necessário definir a sentença SQL e criar um objeto da classe OdbcCommand. 1 // definindo a sentença SQL 2 string textoDoComando = @ " SELECT * FROM Editoras ; " ; 3 4 // criando um comando odbc 5 OdbcCommand comando = new OdbcCommand ( textoDoComando , conexao ) ; Código C# 2.15: Criando um comando de seleção A diferença é que para executar um comando de consulta é necessário utilizar o método Execu- 30 www.k19.com.br
  • 41.
    31 ADO.NET teReader() ao invés do ExecuteNonQuery(). Esse método devolve um objeto da classe System.Da- ta.Odbc.OdbcDataReader. 1 OdbcDataReader resultado = comando . ExecuteReader () ; Código C# 2.16: Executando um comando de consulta Os dados contidos no OdbcDataReader podem ser acessados através dos nomes das colunas. 1 string nome = resultado [ " Nome " ] as string ; 2 string email = resultado [ " Email " ] as string ; Código C# 2.17: Recuperando os campos do primeiro registro do resultado O código acima mostra como os campos do primeiro registro do resultado da consulta são recu- perados. Agora, para recuperar os outros registros é necessário avançar o OdbcDataReader através do método Read(). 1 string nome1 = resultado [ " nome " ] as string ; 2 string email1 = resultado [ " email " ] as string ; 3 4 resultado . Read () ; 5 6 string nome2 = resultado [ " nome " ] as string ; 7 string email2 = resultado [ " email " ] as string ; Código C# 2.18: Recuperando os campos dos dois primeiros registros do resultado O próprio método Read() devolve um valor booleano para indicar se o reader conseguiu avançar para o próximo registro. Quando esse método devolver false significa que não há mais registros para serem recuperados. 1 while ( resultado . Read () ) 2 { 3 string nome = resultado [ " nome " ] as string ; 4 string email = resultado [ " email " ] as string ; 5 } Código C# 2.19: Recuperando os campos de todos os registros do resultado Exercícios de Fixação 6 Insira algumas editoras utilizando a classe InsereEditora que você criou nos exercícios anteri- ores. 7 Adicione uma nova classe ao projeto ODBC chamada ListaEditora. O objetivo é listar as edi- toras que foram salvas no banco. Veja o código dessa classe. 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaEditora 6 { 7 static void Main ( string [] args ) 8 { www.k19.com.br 31
  • 42.
    ADO.NET 32 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 13 { 14 string textoListaEditora = " SELECT * FROM Editoras " ; 15 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 16 conexao . Open () ; 17 OdbcDataReader resultado = command . ExecuteReader () ; 18 19 while ( resultado . Read () ) 20 { 21 long ? id = resultado [ " Id " ] as long ?; 22 string nome = resultado [ " Nome " ] as string ; 23 string email = resultado [ " Email " ] as string ; 24 System . Console . WriteLine ( " {0} : {1} - {2} n " ,id , nome , email ) ; 25 } 26 } 27 } 28 } 29 } Código C# 2.20: ListaEditora.cs Exercícios Complementares 5 Crie uma classe para listar os livros cadastrados na base de dados. Connection Factory Você deve ter percebido que para cada ação executada no banco de dados, nós precisamos criar uma conexão. Isso gera um problema relacionado à string de conexão ficar armazenada em diversos locais. Imagine que o driver do banco foi atualizado e mudamos a sua versão. Isso implicaria fa- zer diversas alterações no código em cada ocorrência da string de conexão, tornando o código mais suscetível a erros e dificultando a sua manutenção. Para resolver esta situação, nós poderíamos criar uma classe responsável pela criação e distri- buição de conexões, mantendo assim uma única referência para a string de conexão, e qualquer alteração no modo em que nos conectamos à base de dados, só implica mudanças nesta classe. 1 static class ConnectionFactory 2 { 3 public static OdbcConnection CreateConnection () 4 { 5 string driver = @ " SQL Server " ; 6 string servidor = @ " MARCELO - PC SQLEXPRESS " ; 7 string baseDeDados = @ " livraria " ; 8 string usuario = @ " sa " ; 9 string senha = @ " sa " ; 10 11 StringBuilder connectionString = new StringBuilder () ; 12 connectionString . Append ( " driver = " ) ; 13 connectionString . Append ( driver ) ; 14 connectionString . Append ( " ; server = " ) ; 15 connectionString . Append ( servidor ) ; 16 connectionString . Append ( " ; database = " ) ; 17 connectionString . Append ( baseDeDados ) ; 18 connectionString . Append ( " ; uid = " ) ; 32 www.k19.com.br
  • 43.
    33 ADO.NET 19 connectionString . Append ( usuario ) ; 20 connectionString . Append ( " ; pwd = " ) ; 21 connectionString . Append ( senha ) ; 22 23 return new OdbcConnection ( connectionString . ToString () ) ; 24 } 25 } Código C# 2.22: ConnectionFactory.cs Agora podemos obter uma nova conexão apenas chamando ConnectionFactory.CreateConnection(). O resto do sistema não precisa mais conhecer os detalhes sobre a conexão com o banco de dados, diminuindo o acoplamento da aplicação. Exercícios de Fixação 8 Adicione uma nova classe chamada ConnectionFactory com seguinte código: 1 using System ; 2 using System . Data . Odbc ; 3 using System . Text ; 4 5 namespace Odbc 6 { 7 static class ConnectionFactory 8 { 9 public static OdbcConnection CreateConnection () 10 { 11 string driver = @ " SQL Server " ; 12 string servidor = @ " MARCELO - PC SQLEXPRESS " ; 13 string baseDeDados = @ " livraria " ; 14 string usuario = @ " sa " ; 15 string senha = @ " sa " ; 16 17 StringBuilder connectionString = new StringBuilder () ; 18 connectionString . Append ( " driver = " ) ; 19 connectionString . Append ( driver ) ; 20 connectionString . Append ( " ; server = " ) ; 21 connectionString . Append ( servidor ) ; 22 connectionString . Append ( " ; database = " ) ; 23 connectionString . Append ( baseDeDados ) ; 24 connectionString . Append ( " ; uid = " ) ; 25 connectionString . Append ( usuario ) ; 26 connectionString . Append ( " ; pwd = " ) ; 27 connectionString . Append ( senha ) ; 28 29 return new OdbcConnection ( connectionString . ToString () ) ; 30 } 31 } 32 } Código C# 2.23: ConnectionFactory.cs 9 Altere as classes InsereEditora e ListaEditora para que elas utilizem a fábrica de conexão. Execute-as novamente. 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereEditora 6 { www.k19.com.br 33
  • 44.
    ADO.NET 34 7 static void Main ( string [] args ) 8 { 9 System . Console . Write ( " Digite o Nome da Editora : " ) ; 10 string nome = System . Console . ReadLine () ; 11 12 System . Console . Write ( " Digite o Email da Editora : " ) ; 13 string email = System . Console . ReadLine () ; 14 15 string textoInsereEditora = 16 @ " INSERT INTO Editoras ( Nome , Email ) VALUES (? ,?) " ; 17 18 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 19 { 20 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 21 22 command . Parameters . AddWithValue ( " @Nome " , nome ) ; 23 command . Parameters . AddWithValue ( " @Email " , email ) ; 24 25 conexao . Open () ; 26 command . ExecuteNonQuery () ; 27 } 28 } 29 } 30 } Código C# 2.24: InsereEditora.cs 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaEditora 6 { 7 static void Main ( string [] args ) 8 { 9 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 10 { 11 string textoListaEditora = " SELECT * FROM Editoras " ; 12 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 13 conexao . Open () ; 14 OdbcDataReader resultado = command . ExecuteReader () ; 15 16 while ( resultado . Read () ) 17 { 18 long ? id = resultado [ " Id " ] as long ?; 19 string nome = resultado [ " Nome " ] as string ; 20 string email = resultado [ " Email " ] as string ; 21 System . Console . WriteLine ( " {0} : {1} - {2} n " ,id , nome , email ) ; 22 } 23 } 24 } 25 } 26 } Código C# 2.25: ListaEditora.cs Exercícios Complementares 6 Altere as classes InsereLivro e ListaLivro para que elas utilizem a fábrica de conexão. Execute- as novamente. 34 www.k19.com.br
  • 45.
    35 ADO.NET Desafios 1 Implemente um teste que remove uma editora pelo id. 2 Implemente um teste que altera os dados de uma editora pelo id. www.k19.com.br 35
  • 46.
    ADO.NET 36 36 www.k19.com.br
  • 47.
    CAPÍTULO ENTITY F RAMEWORK 3 Múltiplas sintaxes da linguagem SQL No capítulo anterior, utilizamos conexões ODBC para fazer uma aplicação C# interagir com os SGDBs. Nessa interação, as consultas são definidas com a linguagem SQL. A sintaxe dessa linguagem é diferente em cada SGDB. Dessa forma, a complexidade do trabalho dos desenvolvedores aumenta. Para resolver esse problema, as consultas deveriam ser definidas através de um mecanismo indepen- dente da linguagem SQL. SELECT TOP 100 * FROM livros ORDER BY autor ASC Driver SQL Server ODBC SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY autor ASC) AS rownumber, id, titulo, autor FROM livros ) WHERE rownumber <= 100 Driver Oracle ODBC Figura 3.1: Diferentes sintaxes da linguagem SQL Orientação a Objetos VS Modelo Relacional Outro problema na comunicação entre uma aplicação C# e um SGDB é o conflito de paradigmas. Os SGDBs são organizados seguindo o modelo relacional. Por outro lado, as aplicações C# utilizam www.k19.com.br 37
  • 48.
    E NTITY FRAMEWORK 38 o modelo orientado a objetos. A transição de dados entre o modelo relacional e o modelo orientado a objetos não é simples. Para realizar essa transição, é necessário definir um mapeamento entre os conceitos desses dois pa- radigmas. Por exemplo, classes podem ser mapeadas para tabelas, objetos para registros, atributos para campos e referência entre objetos para chaves estrangeiras. tbl_editoras Livro id nome id = 1 1 Cultura titulo = Os Lusíadas 2 FTDA autor = Luís Vaz de Camões 3 Globo editora = 1 4 Scipione Editora id = 1 nome = Livraria Cultura tbl_livros Livro id = 2 id titulo autor editora_id titulo = Vidas Secas 1 Os Lusíadas Luís Vaz de Camões 1 autor = Graciliano Ramos 2 Vidas Secas Graciliano Ramos 1 editora = 1 3 Dom Casmurro Machado de Assis 3 4 O Cortiço Aluísio Azevedo 2 ORIENTAÇÃO A OBJETOS MODELO RELACIONAL Figura 3.2: Modelo Orientado a Objetos vs Modelo Relacional Ferramentas ORM Para facilitar a comunicação entre aplicações C# que seguem o modelo orientado a objetos e os SGDBs que seguem o modelo relacional, podemos utilizar ferramentas que automatizam a transição de dados entre as aplicações e os SGDBs. Essas ferramentas são conhecidas como ferramentas ORM (Object Relational Mapper). As ferramentas ORM oferecem mecanismos de consultas independentes da linguagem SQL. Dessa forma, o acoplamento entre as aplicações e os SGDBs diminui drasticamente. As principais ferra- mentas ORM para C# utilizadas no mercado são o Entity Framework e o NHibernate. 38 www.k19.com.br
  • 49.
    39 E NTITY F RAMEWORK tbl_livros id titulo autor editora_id Livro Livro 1 Os Lusíadas Luís Vaz de Camões 1 FERRAMENTA id = 1 id = 2 2 Vidas Secas Graciliano Ramos 1 ORM titulo = Os Lusíadas autor = Luís Vaz de titulo = Vidas Secas autor = Graciliano 3 Dom Casmurro Machado de Assis 3 Camões Ramos 4 O Cortiço Aluísio Azevedo 2 editora = 1 editora = 1 Figura 3.3: Transformação dos dados do Modelo Relacional para o Modelo Orientado a Objetos tbl_livros Livro Livro id titulo autor editora_id id = 1 id = 2 FERRAMENTA 1 Os Lusíadas Luís Vaz de Camões 1 titulo = Os Lusíadas autor = Luís Vaz de titulo = Vidas Secas autor = Graciliano ORM Vidas Secas 2 Graciliano Ramos 1 Camões Ramos 3 Dom Casmurro Machado de Assis 3 editora = 1 editora = 1 4 O Cortiço Aluísio Azevedo 2 Figura 3.4: Transformação dos dados do Modelo Orientado a Objetos para o Modelo Relacional Nesse curso usaremos o Entity Framework 4.3.1, que pode ser obtido em http://www.k19.com. br/EntityFramework.dll ou http://nuget.org/packages/EntityFramework/4.3.1. Configuração Para configurar o Entity Framework em uma aplicação, podemos criar um arquivo chamado App.config. O conteúdo desse arquivo possuirá informações sobre o banco de dados, como a url de conexão, usuário e senha. Veja abaixo um exemplo de configuração para o App.config. 1 <? xml version = " 1.0 " ? > 2 < configuration > 3 < connectionStrings > 4 < add 5 name = " K19Context " 6 providerName = " System . Data . SqlClient " 7 connectionString = " Server =. SQLEXPRESS ; 8 Database = k19db ; 9 Trusted_Connection = false ; 10 User Id = sa ; 11 Password = sa ; 12 Persist Security Info = true ; 13 MultipleActiveResultSets = True " / > 14 </ connectionStrings > 15 </ configuration > Código XML 3.1: App.config www.k19.com.br 39
  • 50.
    E NTITY FRAMEWORK 40 Mapeamento Um dos principais objetivos dos frameworks ORM é estabelecer o mapeamento entre os concei- tos do modelo orientado a objetos e os conceitos do modelo relacional. Este mapeamento pode ser definido através de um arquivo XML ou de maneira mais prática com DbContext. Quando utiliza- mos DbContext, evitamos a criação de extensos arquivos XML. Considere as entidades Livro e Editora definidas abaixo. 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 public string Titulo { get ; set ; } 5 public decimal Preco { get ; set ; } 6 public Editora Editora { get ; set ; } 7 } Código C# 3.1: Livro.cs 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public ICollection < Livro > Livros { get ; set ; } 7 } Código C# 3.2: Editora.cs Criaremos uma classe para ajudar a mapear essas entidades para o banco de dados. A classe EditoraContext deriva de DbContext, que faz parte da biblioteca Code First: 1 public class EditoraContext : DbContext 2 { 3 public DbSet < Editora > Editoras { get ; set ; } 4 public DbSet < Livro > Livros { get ; set ; } 5 } Código C# 3.3: EditoraContext.cs Utilizamos o recurso Code First do Entity Framework 4 para permitir a persistência no banco de dados. Isto significa que as propriedades Editoras e Livros de EditoraContext serão mapeadas para tabelas com mesmo nome no banco de dados. Ou seja, a propriedade Editoras será mapeada para uma tabela chamada Editoras e a propriedade Livros será mapeada para uma tabela chamada Livros. Cada propriedade definida nas entidades Livro e Editora será mapeada para uma coluna nas tabelas Livros e Editoras. Abaixo segue a definição da tabela Editoras que foi criada em nosso banco de dados: 40 www.k19.com.br
  • 51.
    41 E NTITY F RAMEWORK Abaixo segue a definição da tabela Livros que foi criada em nosso banco de dados: Utilizando o Code First, não foi necessário configurar nada para que a persistência e o mapea- mento fossem realizados. Isto ocorreu simplesmente escrevendo as três classes acima. Nenhuma configuração a mais é necessária. Podemos utilizar anotações para sobrescrever o mapeamento padrão. Para utilizar anotações, precisamos adicionar como referência as bibliotecas EntityFramework.dll e System.Component- Model.DataAnnotations.dll ao projeto e acrescentar using para o namespace System.Component- Model.DataAnnotations. A seguir descrevemos as principais anotações. ColumnAttribute: Define o nome e o tipo da coluna no banco de dados da propriedade mapeada. 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 [ Column ( " NomeDoLivro " , TypeName = " varchar " ) ] 5 public string Titulo { get ; set ; } 6 public decimal Preco { get ; set ; } 7 public Editora Editora { get ; set ; } 8 } Código C# 3.4: Livro.cs DatabaseGeneratedAttribute: Utilizado para indicar que o valor do atributo é gerado automatica- mente pelo banco de dados. Para definir como o valor do atributo é gerado, podemos utilizar três constantes do enum DatabaseGenerateOption: • DatabaseGeneratedOption.Identity: o valor de um atributo com a opção Identity deve ser gerado quando a instância for salva (com a chamada de SaveChanges()) pela primeira vez. Além disso, o banco de dados supõe que esse valor não será mais alterado. Portanto, não se deve modificar o valor desse atributo. • DatabaseGeneratedOption.Computed: o valor de um atributo com a opção Computed é gerado sempre que a instância for salva. • DatabaseGeneratedOption.None: o valor de um atributo com a opção None não será ge- rado pelo banco de dados. ForeignKeyAttribute: Define que o atributo será mapeado para uma chave estrangeira. 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 public string Titulo { get ; set ; } www.k19.com.br 41
  • 52.
    E NTITY FRAMEWORK 42 5 public decimal Preco { get ; set ; } 6 [ ForeignKey ( " Editora " ) ] 7 public int EditoraId { get ; set ; } 8 public Editora Editora { get ; set ; } 9 } Código C# 3.5: Livro.cs 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public ICollection < Livro > Livros { get ; set ; } 7 } Código C# 3.6: Editora.cs InversePropertyAttribute: Nos relacionamentos bidirecionais, utilizamos essa anotação para evitar o mapeamento de dois relacionamentos unidirecionais ao invés de um bidirecional. Por exemplo, considere as entidades Pessoa e Livro. Podemos estabelecer um relacionamento bidirecional entre essas entidades, pois uma pessoa pode escrever um livro. 1 public class Pessoa 2 { 3 public int Id { get ; set ; } 4 public string Nome { get ; set ; } 5 public ICollection < Livro > LivrosPublicados { get ; set ; } 6 } Código C# 3.7: Pessoa.cs 1 public class Livro 2 { 3 public int LivroId { get ; set ; } 4 public string Titulo { get ; set ; } 5 public decimal Preco { get ; set ; } 6 public Editora Editora { get ; set ; } 7 8 [ InverseProperty ( " LivrosPublicados " ) ] 9 public Pessoa Autor { get ; set ; } 10 } Código C# 3.8: Livro.cs No código acima, a propriedade Autor da classe Livro foi mapeada com a anotação Inverse- Property para evitar que dois relacionamentos unidirecionais fossem estabelecidos entre Pes- soa e Livro. KeyAttribute: Define uma ou mais propriedades que identificam unicamente as instâncias de uma entidade. Se a classe define propriedades chamadas ID ou Id, ou com o nome da classe seguido por ID ou Id, esta propriedade é tratada como chave primária por convenção. Caso contrário, podemos definir a nossa chave primária com o KeyAttribute. No exemplo abaixo, definimos a propriedade Identificador para ser a chave primária correspondente à entidade Pessoa. 1 public class Pessoa 2 { 42 www.k19.com.br
  • 53.
    43 E NTITY F RAMEWORK 3 [ Key ] 4 public int Identificador { get ; set ; } 5 public string Nome { get ; set ; } 6 public ICollection < Livro > LivrosPublicados { get ; set ; } 7 public ICollection < Livro > LivrosRevisados { get ; set ; } 8 } Código C# 3.9: Pessoa.cs MaxLengthAttribute: Define o tamanho máximo permitido para um array ou string. MinLengthAttribute: Define o tamanho mínimo permitido para um array ou string. 1 public class Pessoa 2 { 3 public int Id { get ; set ; } 4 [ MinLength (10 , ErrorMessage = " Tamanho minimo : 10 " ) ] 5 [ MaxLength (255 , ErrorMessage = " Tamanho máximo : 255 " ) ] 6 public string Nome { get ; set ; } 7 public ICollection < Livro > LivrosPublicados { get ; set ; } 8 public ICollection < Livro > LivrosRevisados { get ; set ; } 9 } Código C# 3.10: Pessoa.cs StringLengthAttribute: Define os tamanhos mínimo e máximo permitido para o campo string. 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 [ StringLength (255 , MinimumLength =10) ] 5 public string Nome { get ; set ; } 6 public string Email { get ; set ; } 7 public string ExtraInfo { get ; set ; } 8 public ICollection < Livro > Livros { get ; set ; } 9 } Código C# 3.11: Editora.cs NotMappedAttribute: Pode ser aplicado em classes ou propriedades. Indica que a classe ou propri- edade anotada não deve ser mapeada para o banco de dados. 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 [ NotMapped ] 7 public string ExtraInfo { get ; set ; } 8 public ICollection < Livro > Livros { get ; set ; } 9 } Código C# 3.12: Editora.cs RequiredAttribute: Define que o campo é obrigatório. Este atributo é ignorado em propriedades do tipo collection. Quando definido numa referência, indica que a cardinalidade é 1 e a proprie- dade da chave estrangeira é não-nula. 1 public class Editora 2 { 3 public int EditoraId { get ; set ; } 4 [ Required ] www.k19.com.br 43
  • 54.
    E NTITY FRAMEWORK 44 5 public string Nome { get ; set ; } 6 public string Email { get ; set ; } 7 public string ExtraInfo { get ; set ; } 8 public ICollection < Livro > Livros { get ; set ; } 9 } Código C# 3.13: Editora.cs TableAttribute: Define a tabela para a qual a classe deve ser mapeada. 1 [ Table ( " Livros " ) ] 2 public class Livro 3 { 4 public int LivroId { get ; set ; } 5 public string Titulo { get ; set ; } 6 public decimal Preco { get ; set ; } 7 public Editora Editora { get ; set ; } 8 public Pessoa Autor { get ; set ; } 9 public Pessoa Revisor { get ; set ; } 10 } Código C# 3.14: Livro.cs Manipulando entidades Mostramos anteriormente que as entidades de uma aplicação são mapeadas através de uma classe que deriva de DbContext. Considere o seguinte exemplo. Suponha que as entidades da nossa aplicação foram mapeadas com a classe K19Context, que deriva de DbContext. As instâncias das entidades da nossa aplicação serão administradas por objetos da classe K19Context. Esses objetos são responsáveis pelas opera- ções de inserção, remoção, alteração e consulta. Mostraremos a seguir as principais operações que podem ser realizadas através de um DbContext. Persistindo Para indicar que determinado objeto deve ser persistido no banco de dados, devemos utilizar o método System.Data.Entity.DbSet.Add(), passando o objeto em questão como parâmetro. O método Add() adiciona o objeto ao contexto com o estado Added. Para armazenar de fato as informações de um objeto no banco de dados, utilizamos o método SaveChanges() do DbContext. Todos os objetos do contexto que estejam no estado Added são inse- ridos no banco de dados quando o método SaveChanges() é chamado. 1 using ( var context = new EditoraContext () ) 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 // Adiciona a editora criada ao contexto com estado Added 5 context . Editoras . Add ( editora ) ; 6 context . SaveChanges () ; 7 } Código C# 3.15: Persistindo um objeto 44 www.k19.com.br
  • 55.
    45 E NTITY F RAMEWORK Buscando Para obter um objeto que contenha informações do banco de dados, basta utilizar o método Find() do DbSet. Podemos passar o identificador do objeto que desejamos recuperar como parâ- metro para esse método. 1 Editora editora = context . Editoras . Find (1) ; Código C# 3.16: Buscando uma editora com o identificador 1 Removendo Para indicar que determinado objeto deve ser removido, basta utilizar o método System.Da- ta.Entity.DbSet.Remove(). Esse método marca o objeto para ser removido do banco de dados, colocando-o no estado Deleted. Quando o método SaveChanges() é chamado, todas os objetos no estado Deleted são removidos do banco de dados. 1 Editora editora = context . Editoras . Find (1) ; 2 context . Editoras . Remove ( editora ) ; 3 context . SaveChanges () ; Código C# 3.17: Removendo uma editora do banco de dados Atualizando Para alterar os dados de um registro correspondente a um objeto, basta utilizar as suas proprieda- des. Quando as propriedades de um objeto do contexto são alteradas, o estado Modified é associado a este objeto. Objetos no estado Modified são atualizados no banco de dados quando o método SaveChanges() é chamado. 1 Editora editora = context . Editoras . Find (1) ; 2 editora . Nome = " Abril S / A " ; 3 context . SaveChanges () ; Código C# 3.18: Alterando o nome de uma editora Listando Para obter uma listagem com todos os objetos referentes aos registros de uma tabela, podemos utilizar o Language Integrated Query (LINQ), que nos permite escrever consultas dentro do código C#. 1 var consulta = from e in context . Editoras 2 where e . Nome . Contains ( " A " ) 3 select e ; 4 5 // Equivalente a : SELECT * FROM Editoras e where e . Nome Like ’A % ’ 6 foreach ( var item in consulta ) www.k19.com.br 45
  • 56.
    E NTITY FRAMEWORK 46 7 { 8 System . Console . WriteLine ( " Editora : " + item . Nome ) ; 9 } Código C# 3.19: Utilizando LINQ para fazer uma consulta 46 www.k19.com.br
  • 57.
    47 E NTITY F RAMEWORK Exercícios de Fixação 1 Crie um projeto do tipo Console Application no Microsoft Visual C# Express, chamado EF4-Code-First. 2 Adicione ao projeto as bibliotecas EntityFramework.dll e System.ComponentModel.Data- Annotations.dll. Figura 3.5: Adicionando uma referência Faça o download da biblioteca EntityFramework.dll no site da K19 (http://www.k19.com.br/ EntityFramework.dll) e então adicione-a ao projeto: Figura 3.6: Adicionando EntityFramework.dll ao projeto www.k19.com.br 47
  • 58.
    E NTITY FRAMEWORK 48 Adicione a biblioteca System.ComponentModel.DataAnnotations.dll ao projeto: Figura 3.7: Adicionando System.ComponentModel.DataAnnotations.dll ao projeto 3 Defina uma classe Editora conforme código abaixo: 1 namespace EF4_Code_First 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 } 9 } Código C# 3.20: Editora.cs 4 Defina uma classe LivrariaContext que é derivada de DbContext. Nesta classe defina uma propriedade Editoras do tipo genérico DbSet<Editora>. 1 using System . Data . Entity ; 2 3 namespace EF4_Code_First 4 { 5 public class LivrariaContext : DbContext 6 { 7 public DbSet < Editora > Editoras { get ; set ; } 8 } 9 } Código C# 3.21: LivrariaContext.cs 5 Adicione o arquivo App.config e defina a string de conexão. 48 www.k19.com.br
  • 59.
    49 E NTITY F RAMEWORK Figura 3.8: App.config 1 <? xml version = " 1.0 " encoding = " utf -8 " ? > 2 < configuration > 3 < connectionStrings > 4 < add 5 name = " LivrariaContext " 6 providerName = " System . Data . SqlClient " 7 connectionString = " Server =. SQLEXPRESS ; 8 Database = efcodefirst ; 9 Trusted_Connection = false ; 10 User Id = sa ; 11 Password = sa ; 12 Persist Security Info = true ; 13 MultipleActiveResultSets = True " / > 14 </ connectionStrings > 15 </ configuration > Código XHTML 3.1: Arquivo App.config 6 Defina uma classe InsereEditora: 1 using System ; 2 3 namespace EF4_Code_First 4 { 5 public class InsereEditora 6 { 7 static void Main ( string [] args ) 8 { 9 using ( LivrariaContext db = new LivrariaContext () ) 10 { 11 Console . WriteLine ( " Digite o nome da Editora : " ) ; 12 string nome = Console . ReadLine () ; 13 Console . WriteLine ( " Digite o email da Editora : " ) ; 14 string email = Console . ReadLine () ; 15 Editora e = new Editora { Nome = nome , Email = email }; 16 db . Editoras . Add ( e ) ; www.k19.com.br 49
  • 60.
    E NTITY FRAMEWORK 50 17 db . SaveChanges () ; 18 } 19 } 20 } 21 } Código C# 3.22: InsereEditora.cs 7 Defina a classe ListaEditoras: 1 using System . Linq ; 2 using System ; 3 4 namespace EF4_Code_First 5 { 6 public class ListaEditoras 7 { 8 static void Main ( string [] args ) 9 { 10 using ( LivrariaContext db = new LivrariaContext () ) 11 { 12 var consulta = from e in db . Editoras 13 select e ; 14 foreach ( Editora editora in consulta ) 15 { 16 Console . WriteLine ( " {0} - {1} {2} " , editora . Id , editora . Nome , editora . Email ) ; 17 } 18 } 19 } 20 } 21 } Código C# 3.23: classe ListaEditoras Repositórios As classes DbContext e DbSet do EF oferecem recursos suficientes para que os objetos do domí- nio sejam persistidos, recuperados, alterados e removidos do banco de dados. Porém, em aplicações com alta complexidade e grande quantidade de código, “espalhar” as chamadas aos métodos do DbContext e DbSet pode gerar dificuldades na manutenção e no entendimento do sistema. Para melhorar a organização das nossas aplicações, diminuindo o custo de manutenção e au- mentando a legibilidade do código, podemos aplicar o padrão Repository do DDD (Domain Driven Design). Conceitualmente, um repositório representa o conjunto de todos os objetos de um determinado tipo. Ele deve oferecer métodos para administrar seus elementos. Os repositórios podem trabalhar com objetos prontos na memória ou reconstruí-los com dados obtidos de um banco de dados. O acesso ao banco de dados pode ser realizado através de ferramenta ORM como o Entity Framework. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Data . Entity ; 5 6 namespace EF4_Code_First 7 { 50 www.k19.com.br
  • 61.
    51 E NTITY F RAMEWORK 8 public class EditoraRepository 9 { 10 DbContext context ; 11 12 public EditoraRepository ( DbContext context ) 13 { 14 this . context = context ; 15 } 16 17 public void Adiciona ( Editora e ) 18 { 19 context . Set < Editora >() . Add ( e ) ; 20 context . SaveChanges () ; 21 } 22 23 public Editora Busca ( int id ) 24 { 25 return context . Set < Editora >() . Find ( id ) ; 26 } 27 28 public List < Editora > BuscaTodas () 29 { 30 var consulta = from e in context . Set < Editora >() 31 select e ; 32 return consulta . ToList () ; 33 } 34 } 35 } Código C# 3.24: EditoraRepository.cs 1 var context = new EditoraContext () ; 2 3 EditoraRepository repository = new EditoraRepository ( context ) ; 4 5 List < Editora > editoras = repository . BuscaTodas () ; Código C# 3.25: Buscando todas as editoras armazenadas no banco de dados Exercícios de Fixação 8 Implemente um repositório de editoras criando uma nova classe no projeto EF4-Code-First. 1 using System ; 2 using System . Linq ; 3 using System . Collections . Generic ; 4 5 namespace EF4_Code_First 6 { 7 public class EditoraRepository : IDisposable 8 { 9 private LivrariaContext db = new LivrariaContext () ; 10 11 public void Adiciona ( Editora e ) 12 { 13 db . Editoras . Add ( e ) ; 14 db . SaveChanges () ; 15 } 16 17 public Editora Busca ( int id ) 18 { 19 Editora e = db . Editoras . Find ( id ) ; 20 return e ; 21 } www.k19.com.br 51
  • 62.
    E NTITY FRAMEWORK 52 22 23 public List < Editora > BuscaTodas () 24 { 25 var consulta = from e in db . Editoras 26 select e ; 27 return consulta . ToList () ; 28 } 29 30 public void Dispose () 31 { 32 db . Dispose () ; 33 } 34 } 35 } Código C# 3.26: EditoraRepository.cs 9 Altere a classe InsereEditora para que utilize o repositório de editoras. 1 using System ; 2 3 namespace EF4_Code_First 4 { 5 public class InsereEditora 6 { 7 static void Main ( string [] args ) 8 { 9 using ( EditoraRepository repository = new EditoraRepository () ) { 10 Console . WriteLine ( " Digite o nome da Editora : " ) ; 11 string nome = Console . ReadLine () ; 12 Console . WriteLine ( " Digite o email da Editora : " ) ; 13 string email = Console . ReadLine () ; 14 Editora e = new Editora { Nome = nome , Email = email }; 15 repository . Adiciona ( e ) ; 16 } 17 } 18 } 19 } Código C# 3.27: InsereEditora.cs 10 Altere a classe ListaEditoras para que utilize o repositório de editoras. 1 using System ; 2 3 namespace EF4_Code_First 4 { 5 public class ListaEditoras 6 { 7 static void Main ( string [] args ) 8 { 9 using ( EditoraRepository repository = new EditoraRepository () ) { 10 var consulta = repository . BuscaTodas () ; 11 foreach ( Editora editora in consulta ) 12 { 13 Console . WriteLine ( " {0} - {1} {2} " , editora . Id , editora . Nome , editora . Email ) ; 14 } 15 } 16 } 17 } 18 } Código C# 3.28: ListaEditoras.cs 52 www.k19.com.br
  • 63.
    CAPÍTULO VISÃO G ERAL DO ASP.NET MVC 4 Necessidades de uma aplicação web HTTP Os usuários de uma aplicação web utilizam navegadores (browsers) para interagir com essa apli- cação. A comunicação entre navegadores e uma aplicação web é realizada através de requisições e respostas definidas pelo protocolo HTTP Dessa forma, os desenvolvedores de aplicações web devem . estar preparados para trabalhar com o protocolo HTTP. Acesso simultâneo Além disso, na grande maioria dos casos, as aplicações web devem ser acessadas por diversos usuários ao mesmo tempo. Consequentemente, os desenvolvedores web devem criar ou utilizar algum mecanismo eficiente que permita esse tipo de acesso. Conteúdo dinâmico As páginas de uma aplicação web devem ser geradas dinamicamente. Por exemplo, quando um usuário de uma aplicação de email acessa a sua caixa de entrada, ele deseja ver todos os emails enviados até aquele momento. A página contendo a lista de emails deve ser gerada novamente toda vez que essa página for requisitada. Consequentemente, os desenvolvedores web devem criar ou utilizar algum mecanismo eficiente que permita que o conteúdo das páginas das aplicações web seja gerado dinamicamente. www.k19.com.br 53
  • 64.
    V ISÃO GERAL DO ASP.NET MVC 54 Aplicação Web TP Re HT sp Requisição HTTP Resposta HTTP o ão st ç a isi HT qu TP Re TP Re HT sp o o st çã aH isi qu TT Re P www.k19.com.br www.k19.com.br www.k19.com.br Cursos Artigos Apostilas Figura 4.1: Necessidades de uma aplicação web Solução Resolver os três problemas apresentados tomaria boa parte do tempo de desenvolvimento, além de exigir conhecimentos técnicos extremamente específicos por parte dos desenvolvedores. Para facilitar o desenvolvimento de aplicações web, a plataforma .NET soluciona esses problemas funda- mentais. A plataforma .NET oferece os seguintes recursos fundamentais para o desenvolvimento de apli- cações web: • Envio e recebimento de mensagens HTTP. • Acesso simultâneo das aplicações por vários usuários de uma maneira eficiente. • Geração dinâmica das páginas das aplicações. ASP.NET MVC Atualmente, o ASP.NET MVC é o framework para desenvolvimento de aplicações web na pla- taforma .NET em maior ascensão. A documentação desse framework pode ser obtida em http: //www.asp.net/mvc. O ASP.NET MVC é fortemente baseado nos padrões MVC e Front Controller. MVC e Front Controller O MVC (model-view-controller) é um padrão de arquitetura que tem por objetivo isolar a lógica de negócio da lógica de apresentação de uma aplicação. 54 www.k19.com.br
  • 65.
    55 V ISÃO G ERAL DO ASP.NET MVC Esse padrão (ou alguma variação) é amplamente adotado nas principais plataformas de desen- volvimento atuais. Em particular, ele é bastante utilizado no desenvolvimento de aplicações web. O padrão MVC divide uma aplicação em três tipos de componentes: modelo, visão e controlador. Modelo: encapsula os dados e as funcionalidades da aplicação. Visão: é responsável pela exibição de informações, cujos dados são obtidos do modelo. Controlador: recebe as requisições do usuário e aciona o modelo e/ou a visão. Para mais detalhes sobre o padrão MVC, uma boa referência é o livro Pattern-Oriented Software Architecture Volume 1: A System of Patterns (editora Wiley, 1996) dos autores Frank Buschmann, Re- gine Meunier, Hans Rohnert, Peter Sommerlad, Michael Stal e Michael Stal. No padrão Front Controller, todas as requisições do usuário são recebidas pelo mesmo compo- nente. Dessa forma, tarefas que devem ser realizadas em todas as requisições podem ser implemen- tadas por esse componente. Isso evita a repetição de código e facilita a manutenção do sistema. Para mais informações sobre esse padrão, consulte, por exemplo, o livro Professional ASP.NET Design Patterns (editora Wrox, 720) de Scott Millett. Visual Web Developer O primeiro passo para construir uma aplicação web utilizando ASP .NET MVC é criar um projeto no Visual Web Developer a partir do modelo adequado. No nosso caso, o modelo de projeto que deve ser utilizado é o ASP.NET MVC 4 Web Application. Figura 4.2: Criando um projeto www.k19.com.br 55
  • 66.
    V ISÃO GERAL DO ASP.NET MVC 56 Devemos escolher Basic Project conforme figura abaixo: Figura 4.3: Criando um projeto O projeto criado já vem com diversas pastas e arquivos. Ao longo dos próximos capítulos, a fun- ção de cada pasta e de cada arquivo será discutida. Testando a aplicação Para verificar o funcionamento do projeto, basta executá-lo através do menu: Debug -> Start De- bugging. Automaticamente um servidor de desenvolvimento é inicializado na máquina e a aplicação é implantada nesse servidor. Além disso, uma janela do navegador padrão do sistema é aberta na url principal da aplicação. O servidor pode ser finalizado através do ícone ASP.NET Development Server que fica na barra de tarefas do Windows. Trocando a porta do servidor Para trocar a porta utilizada pelo servidor de desenvolvimento que o Visual Web Developer uti- liza, basta alterar as propriedades do projeto clicando com o botão direito do mouse no projeto e escolhendo o item properties e depois a aba web. Exemplo de uma Aplicação Web 56 www.k19.com.br
  • 67.
    57 V ISÃO G ERAL DO ASP.NET MVC Veremos a seguir um exemplo de uma simples aplicação web em ASP.NET MVC. Essa aplicação terá apenas uma página web que mostrará o número de acessos à essa página até o momento. Primeiramente, criaremos uma classe na camada de controle da aplicação para armazenar esse número. As classes que definem controladores devem derivar da classe Controller e seus nomes de- vem terminar com a palavra Controller. Além disso, essas classes devem estar na pasta Controllers da aplicação. Considere a classe K19Controller definida abaixo. 1 public class K19Controller : Controller 2 { 3 private static int NumeroDeAcessos { get ; set ; } 4 5 public ActionResult Home () 6 { 7 K19Controller . NumeroDeAcessos ++; 8 ViewBag . Acessos = K19Controller . NumeroDeAcessos ; 9 return View () ; 10 } 11 } Código C# 4.1: K19Controller.cs A classe K19Controller possui uma propriedade chamada NumeroDeAcessos, que armazenará o número de acessos à nossa página. O método Home() será chamado toda vez que a url http://<IP_SERVIDOR>:<PORTA_APP>/K19/Home for requisitada por um navegador. Note que essa url é formada pelo nome do controlador (nome da classe que define o controlador sem o sufixo Controller) seguido pelo nome desse método. Cada vez que o método Home() é chamado, o valor da propriedade NumeroDeAcessos é incre- mentado. Além disso, o valor atualizado dessa propriedade é colocado na ViewBag para que possa ser acessado na camada de apresentação. Para indicar que o fluxo de execução deve seguir para a camada de apresentação, o método Home() invoca o método View() e devolve a resposta obtida. Por padrão, o fluxo será direcionado para um arquivo chamado Home.cshtml que deve estar localizado na pasta ViewsK19. Na camada de apresentação, vamos adicionar a tela associada ao método Home(). Para isso, cria- remos um arquivo chamado Home.cshtml na pasta ViewsK19 da aplicação com o conteúdo abaixo. 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 < h2 > Acessos : @ViewBag . Acessos </ h2 > Código CSHTML 4.1: Home.cshtml Para acessar a ViewBag, devemos utilizar o caractere @, como no código acima. Exercícios de Fixação 1 Crie um projeto do tipo ASP.NET MVC 4 Web Application chamado K19. Utilize o template Basic www.k19.com.br 57
  • 68.
    V ISÃO GERAL DO ASP.NET MVC 58 Project. Figura 4.4: Criando um projeto Figura 4.5: Criando um projeto 2 Defina uma página de saudação. Para isto, crie um controlador chamado K19 no projeto K19 conforme a figura abaixo: 58 www.k19.com.br
  • 69.
    59 V ISÃO G ERAL DO ASP.NET MVC Figura 4.6: Criando um controlador Figura 4.7: Criando um controlador 1 using System ; 2 using System . Web . Mvc ; 3 4 namespace K19 . Controllers 5 { 6 public class K19Controller : Controller 7 { 8 public ActionResult Home () 9 { 10 Random random = new Random () ; 11 ViewBag . NumeroDaSorte = random . Next () ; 12 return View () ; 13 } 14 } 15 } Código C# 4.2: K19Controller.cs www.k19.com.br 59
  • 70.
    V ISÃO GERAL DO ASP.NET MVC 60 3 Dentro da pasta Views, crie uma pasta chamada K19. Figura 4.8: Criando uma pasta 4 Defina a página com a mensagem de saudação. Crie um arquivo chamado Home.cshtml dentro da pasta ViewsK19. Figura 4.9: Adicionando uma tela 60 www.k19.com.br
  • 71.
    61 V ISÃO G ERAL DO ASP.NET MVC Figura 4.10: Adicionando uma tela 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 < h2 > Olá ! O seu número da sorte é @ViewBag . NumeroDaSorte </ h2 > Código CSHTML 4.2: Home.cshtml 5 Para visualizar a página, basta executar o projeto no Visual Studio e acessar o endereço http://localhost:<PORTA_APP>/K19/Home www.k19.com.br 61
  • 72.
    V ISÃO GERAL DO ASP.NET MVC 62 Figura 4.11: Acessando a página da aplicação Integração com Entity Framework A integração entre Entity Framework e ASP.NET MVC 4 é realizada de maneira extremamente simples. No Visual Studio 2010, ao criar-se um projeto ASP.NET MVC 4 do tipo Basic, a biblioteca do Entity Framework 5 fica disponível automaticamente para o projeto. A string de conexão que a aplicação ASP.NET MVC 4 utilizará deve ser definida no arquivo de configuração Web.config. 1 ... 2 < connectionStrings > 3 < add 4 name = " K19Context " 5 providerName = " System . Data . SqlClient " 6 connectionString = " Server =. SQLEXPRESS ; 7 Database = k19bd ; 8 Trusted_Connection = false ; 9 User Id = sa ; 10 Password = sa ; 11 Persist Security Info = true ; 12 MultipleActiveResultSets = True " / > 13 </ connectionStrings > 14 ... Código XML 4.1: Exemplo de string de conexão Por fim, bastaria implementar as entidades e mapeá-las como visto no Capítulo 3. Scaffold O Visual Studio 2010 é capaz de gerar telas e controladores para as operações de inserção, leitura, alteração e remoção (CRUD) a partir de uma entidade de um projeto ASP.NET MVC 4. Os controla- 62 www.k19.com.br
  • 73.
    63 V ISÃO G ERAL DO ASP.NET MVC dores gerados podem utilizar as funcionalidades do Entity Framework para interagir com o banco de dados. Exercícios de Fixação 6 Vamos gerar o nosso primeiro scaffold. Para isto, defina as classes Editora e K19Context na pasta Models do nosso projeto K19. 1 namespace K19 . Models 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 } 9 } Código C# 4.3: Editora.cs 1 using System . Data . Entity ; 2 3 namespace K19 . Models 4 { 5 public class K19Context : DbContext 6 { 7 public DbSet < Editora > Editoras { get ; set ; } 8 } 9 } Código C# 4.4: K19Context.cs 7 Defina a string de conexão. Para isto, altere o arquivo Web.config da raiz do projeto K19 e acrescente a string de conexão dentro da tag <configuration>. 1 ... 2 < connectionStrings > 3 < add 4 name = " K19Context " 5 providerName = " System . Data . SqlClient " 6 connectionString = " Server =. SQLEXPRESS ; 7 Database = k19bd ; 8 Trusted_Connection = false ; 9 User Id = sa ; 10 Password = sa ; 11 Persist Security Info = true ; 12 MultipleActiveResultSets = True " / > 13 </ connectionStrings > 14 ... Código XML 4.2: String de conexão 8 Antes de gerar o scaffold, precisamos fazer um build no projeto. www.k19.com.br 63
  • 74.
    V ISÃO GERAL DO ASP.NET MVC 64 Figura 4.12: Build do projeto 9 Após o build do projeto, podemos gerar o scaffold. Para isto, devemos criar um controlador chamado Editora conforme a figura abaixo. Figura 4.13: Gerando o scaffold Defina o model que deseja gerar o scaffold e a classe responsável pelo acesso ao banco de dados. 64 www.k19.com.br
  • 75.
    65 V ISÃO G ERAL DO ASP.NET MVC Figura 4.14: Gerando o scaffold Após gerar o scaffold, verifique os arquivos gerados no projeto. www.k19.com.br 65
  • 76.
    V ISÃO GERAL DO ASP.NET MVC 66 Figura 4.15: Scaffold gerado 10 Para testar, acesse o endereço http://localhost:<PORTA_APP>/Editora. 66 www.k19.com.br
  • 77.
    CAPÍTULO C AMADA DE A PRESENTAÇÃO 5 A camada de apresentação é responsável por gerar as páginas de uma aplicação web. Os dados apresentados em uma página web são definidos na camada de modelo. A camada de controle re- cupera os dados da camada de modelo e os envia para a camada de apresentação. Basicamente, a camada de apresentação definirá como esses dados serão apresentados para os usuários da aplica- ção. O fluxo inverso também ocorre. Ou seja, a camada de apresentação obtém dados inseridos pelos usuários e os envia para a camada de controle que, por sua vez, usa esses dados para alterar a camada de modelo. Mostraremos, neste capítulo, como a camada de apresentação de uma aplicação web é cons- truída com a utilização de recursos do ASP.NET MVC como Inline Code, HTML Helper, Layout e Par- tial Views. Razor e ASPX Até a segunda versão do ASP .NET MVC, as telas (ou páginas) de uma aplicação web eram de- senvolvidas em ASPX. A partir da terceira versão desse framework, podemos usar a linguagem Razor para construir essas telas. A principal característica da Razor é ser concisa e simples, diminuindo o número de caracteres e as tradicionais tags de scripts do ASP.NET MVC (<% %>). Nos exemplos abaixo, mostraremos algumas diferença entre Razor e ASPX. Criando Variáveis Toda expressão em Razor começa com o caractere @. Para criarmos variáveis, precisamos declará- las dentro de um bloco Razor: 1 @{ 2 string nome = " K19 " ; 3 string telefoneDaK19 = " 2387 -3791 " ; 4 string enderecoDaK19 = " Av . Brigadeiro Faria Lima " ; 5 int numeroDaK19 = 1571; 6 } Código CSHTML 5.1: Criando variáveis em Razor 1 <% 2 string nome = " K19 " ; 3 string telefoneDaK19 = " 2387 -3791 " ; 4 string enderecoDaK19 = " Av . Brigadeiro Faria Lima " ; 5 int numeroDaK19 = 1571; 6 %> www.k19.com.br 67
  • 78.
    C AMADA DEA PRESENTAÇÃO 68 Código ASPX 5.1: Criando variáveis em ASPX Acessando variáveis As variáveis podem ser facilmente acessadas. 1 <p > Telefone da K19 : @telefoneDaK19 </ p > Código CSHTML 5.2: Acessando uma variável 1 <p > Telefone da K19 : < %= telefoneDaK19 % > </ p > Código ASPX 5.2: Acessando uma variável Condicionais (if e else) Podemos utilizar os comandos de controle de fluxo if e else. Veja os exemplos abaixo. 1 @if ( numero == numeroDaK19 ) 2 { 3 <p > Chegou na K19 ! </ p > 4 } 5 else 6 { 7 <p > Continue andando . </ p > 8 } 9 <! -- ou -- > 10 @{ 11 int numero = 463; 12 if ( numero == numeroDaK19 ) 13 { 14 <p > Chegou na K19 ! </ p > 15 } 16 else 17 { 18 <p > Continue andando . </ p > 19 } 20 } Código CSHTML 5.3: Utilizando if e else em Razor 1 <% 2 if ( numero == numeroDaK19 ) 3 { 4 %> 5 <p > Chegou na K19 ! </ p > 6 <% 7 } 8 else 9 { 10 %> 11 <p > Continue andando . </ p > 12 <% 13 } 14 %> Código CSHTML 5.4: Utilizando if e else em ASPX 68 www.k19.com.br
  • 79.
    69 C AMADA DE A PRESENTAÇÃO Laços Podemos criar laços utilizando o comando for. 1 @for ( int i = 0; i < 5; i ++) 2 { 3 <p >i = @i </ p > 4 } Código CSHTML 5.5: Criando um laço em Razor 1 <% 2 for ( int i = 0; i < 5; i ++) 3 { 4 %> 5 <p >i = < %= i % > </ p > 6 <% 7 } 8 %> Código ASPX 5.3: Criando um laço em ASPX Texto e código 1 @if ( x == " nome " ) 2 { 3 @ : O nome da editora é @editora . Nome 4 } Código CSHTML 5.6: Texto e código em Razor 1 <% 2 if ( x == " nome " ) 3 { 4 %> 5 O nome da editora é < %= editora . Nome % > 6 <% 7 } 8 %> Código ASPX 5.4: Texto e código em ASPX Comentários Podemos adicionar comentários nas páginas. 1 @ * Comentário * @ Código CSHTML 5.7: Comentários em Razor 1 <% -- Comentário --% > Código ASPX 5.5: Comentários em ASPX www.k19.com.br 69
  • 80.
    C AMADA DEA PRESENTAÇÃO 70 Exercícios de Fixação 1 Utilize o projeto K19 e altere o arquivo ViewsK19Home.cshtml para imprimir uma mensagem dez vezes. 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 5 @for ( int i = 0; i < 10; i ++) 6 { 7 < h2 > Olá ! O seu número da sorte é @ViewBag . NumeroDaSorte </ h2 > 8 } Código CSHTML 5.8: Home.cshtml Para ver o resultado, acesse a url http://localhost:<PORTA_APP>/K19/Home. 2 Altere o arquivo Home.cshtml do exercício anterior para imprimir uma mensagem a partir de uma variável. 1 @{ 2 ViewBag . Title = " Home " ; 3 string mensagem = " Olá ! O seu número da sorte é " + @ViewBag . NumeroDaSorte ; 4 } 5 6 @for ( int i = 0; i < 10; i ++) 7 { 8 < h2 > @mensagem </ h2 > 9 } Código CSHTML 5.9: Home.cshtml Para ver o resultado, acesse a url http://localhost:<PORTA_APP>/K19/Home. Exercícios Complementares 1 Altere o arquivo Home.cshtml para gerar a seguinte tela para o usuário: 70 www.k19.com.br
  • 81.
    71 C AMADA DE A PRESENTAÇÃO Figura 5.1: Inline Code ViewBag e Strogly Typed Views A ViewBag é utilizada para transmitir dados da camada de controle para a camada de apresenta- ção. Para adicionar um item à propriedade ViewBag, devemos definir uma chave para esse item. Veja o código abaixo. 1 ViewBag . HoraDoServidor = DateTime . Now . ToShortTimeString () ; Código C# 5.1: Adicionando um item na ViewBag No código acima, criamos uma chave chamada HoraDoServidor e atribuímos um valor a essa chave. Na camada de apresentação, os itens armazenados na ViewBag podem ser acessados facilmente através da sintaxe da Razor. Veja um exemplo abaixo. 1 A hora do servidor é @ViewBag . HoraDoServidor Código CSHTML 5.11: Acessando um item da ViewBag A ViewBag oferece uma forma simples de transmitir dados da camada de controle para a camada de apresentação, mas apresenta algumas desvantagens. Por exemplo, como as chaves são adicio- nadas dinamicamente, não é possível garantir a existência dessas chaves em tempo de compilação. Considere o código abaixo. www.k19.com.br 71
  • 82.
    C AMADA DEA PRESENTAÇÃO 72 1 double numero = new Random () . NextDouble () ; 2 if ( numero < 0.5) 3 { 4 ViewBag . X = " K19 TREINAMENTOS " ; 5 } 6 else 7 { 8 ViewBag . X = 10; 9 } Código C# 5.2: Adcicionando chaves dinamicamente na ViewBag No código acima, o tipo do objeto associado à chave X depende do valor da variável numero, que é gerado aleatoriamente. Quando o valor gerado for menor do que 0.5, o tipo desse objeto será string. Caso contrário, o tipo desse objeto será int. Suponha que a chave X seja utilizada na camada de apresentação. 1 <p > @ViewBag . X . Lower () </ p > Código CSHTML 5.12: Utilizando a chave X ViewBag O código acima não produz erros de compilação. Contudo, na execução, um erro poderá ocorrer. O problema é que esse código supõe que o objeto associado à chave X possui o método Lower(). No entanto, se a ação for acionada um número suficiente de vezes, em algum momento isso não será verdade, e um erro de execução ocorrerá. Além disso, recursos do Visual Studio como IntelliSense (recurso para completar código) ou refa- toração não podem ser aplicados com o uso da ViewBag. Uma alternativa à utilização da ViewBag é o recurso strongly typed views. Com esse recurso podemos fixar, em tempo de compilação, o tipo do objeto que será transferido da camada de controle para a camada de apresentação. Uma vez que o tipo desse objeto foi fixado, o compilador é capaz de verificar a validade do código, evitando eventuais erros de execução. Além disso, o Visual Studio pode auxiliar o desenvolvimento da aplicação com os recursos de IntelliSense e de refatoração. Para utilizar strongly type views, o primeiro passo é, na camada de controle, passar como parâ- metro para o método View() um objeto do tipo esperado pela camada de apresentação. 1 public ActionResult acao () 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View ( editora ) ; 5 } Código C# 5.3: Passando um objeto para a camada de apresentação Para definir o tipo do objeto esperado na camada de apresentação, podemos utilizar a sintaxe da Razor. Veja o código abaixo. 1 @model K19 . Models . Editora Código CSHTML 5.13: Definindo o tipo do objeto esperado na camada de apresentação Podemos também utilizar o comando using. 1 @using K19 . Models 72 www.k19.com.br
  • 83.
    73 C AMADA DE A PRESENTAÇÃO 2 @model Editora Código CSHTML 5.14: Utilizando o comando using Para acessar o objeto transmitido da camada de controle para a camada de apresentação, deve- mos utilizar a propriedade Model. Observe o código a seguir. 1 @using K19 . Models 2 @model Editora 3 4 <p > 5 Nome : @Model . Nome 6 < br / > 7 Email : @Model . Email 8 </ p > Código CSHTML 5.15: Utilizando a propriedade Model Se tentarmos acessar um método ou propriedade inexistentes, um erro de compilação ocorrerá, pois agora o compilador conhece o tipo do objeto transmitido. Dessa forma, o código abaixo produz um erro de compilação. 1 @using K19 . Models 2 @model Editora 3 4 <p > 5 Nome : @Model . Nome 6 < br / > 7 Telefone : @Model . Telefone 8 </ p > Código CSHTML 5.16: Produzindo um erro de compilação Observe que apenas um objeto pode ser transmitido da camada de controle para a camada de apresentação através do recurso strongly type views. Já com a utilização de ViewBag, diversos obje- tos podem ser transmitidos. Note também, que ambos os recursos podem ser utilizados ao mesmo tempo. Exercícios de Fixação 3 Crie uma página que mostra a hora do servidor. No projeto K19, defina um controlador chamado Teste vazio(empty) e uma ação chamada TestaViewBag. Essa ação deve armazenar o horário atual na ViewBag. 1 using System ; 2 using System . Web . Mvc ; 3 4 namespace K19 . Controllers 5 { 6 public class TesteController : Controller 7 { 8 public ActionResult TestaViewBag () 9 { 10 ViewBag . Hora = DateTime . Now . ToShortTimeString () ; 11 return View () ; 12 } 13 } 14 } www.k19.com.br 73
  • 84.
    C AMADA DEA PRESENTAÇÃO 74 Código C# 5.4: TesteController.cs 4 Crie uma página para exibir a hora atual do servidor. Para isto, crie o arquivo TestaView- Bag.cshtml dentro de uma pasta chamada ViewsTeste. 1 @{ 2 ViewBag . Title = " TestaViewBag " ; 3 } 4 5 < h2 > Hora do servidor : @ViewBag . Hora </ h2 > Código HTML 5.1: TestaViewBag.cshtml Para testar, acesse: http://localhost:<PORTA_APP>/Teste/TestaViewBag. 5 Crie uma página para mostrar as informações de um objeto da classe Editora. Este objeto deverá ser acessado através da propriedade Model. Altere o controlador Teste e acrescente a action TestaModel. Crie um objeto da classe Editora e passe-o como referência para a View através da propriedade Model. 1 ... 2 public ActionResult TestaModel () 3 { 4 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 5 return View ( editora ) ; 6 } 7 ... Código C# 5.5: Nova ação do controlador Teste Crie o arquivo TestaModel.cshtml que mostrará as informações do objeto da classe Editora na pasta ViewsTeste do projeto K19. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " TestaModel " ; 5 } 6 7 <p > 8 Nome : @Model . Nome 9 < br / > 10 Email : @Model . Email 11 </ p > Código HTML 5.2: TestaModel.cshtml Para ver o resultado, acesse a url http://localhost:<PORTA_APP>/Teste/TestaModel. HTML Helpers A função das páginas CSHTML é gerar hipertexto XHTML para enviar aos navegadores dos usuá- rios. Os arquivos .cshtml misturam tags XHTML com scripts de servidor escritos em C# (ou outra 74 www.k19.com.br
  • 85.
    75 C AMADA DE A PRESENTAÇÃO linguagem de programação suportada pelo .NET). Essa mistura pode prejudicar a legibilidade do código em alguns casos. Além disso, a manutenção da aplicação pode se tornar mais complexa. Considere a criação de um link utilizando diretamente as tags HTML. 1 <a href = " / Editora / Index / " > Lista de editoras </ a > Código CSHTML 5.17: Link para a lista de editoras Se o formato da url para acessar a ação Index do controlador Editora for alterado, o código acima deverá ser modificado. Veremos como o formato das urls pode ser configurado no Capítulo 7. Para facilitar a manutenção do código das páginas CSHTML, o ASP.NET MVC oferece os chama- dos HTML Helpers. A função de um HTML Helper é encapsular um código XHTML. Por exemplo, para adicionar um link, podemos usar o método ActionLink do objeto Html, ao invés de usar a tag <a> do HTML diretamente. No código abaixo, criamos um link para a ação responsável por listar as editoras. 1 @Html . ActionLink ( " Lista de editoras " , " Index " , " Editora " ) Código CSHTML 5.18: Criando um link para a listagem de editoras Agora, se o formato da url para acessar a ação Index do controlador Editora for alterado, o có- digo acima não precisa ser alterado. ActionLink Helper O helper ActionLink é utilizado para gerar os links das páginas de uma aplicação web. Esse helper pode ser utilizado de diversas maneiras. A forma mais simples de utilizá-lo é passar a ele dois parâmetros: o texto e a ação associados ao link desejado. Nesse caso, o link gerado pelo helper ActionLink estará associado ao controlador correspondente a página na qual o link foi inserido. 1 @Html . ActionLink ( " TEXTO PARA O USUÁRIO " , " ACTION " ) Código CSHTML 5.19: Criando um link com controlador implícito Podemos definir o controlador desejado explicitamente. Para isso, é necessário passar um ter- ceiro parâmetro para o método ActionLink. 1 @Html . ActionLink ( " TEXTO PARA O USUÁRIO " , " ACTION " , " CONTROLADOR " ) Código CSHTML 5.20: Criando um link com controlador explícito O helper ActionLink permite que parâmetros sejam adicionados nos links gerados. Para isso, é necessário passar um array como parâmetro. 1 @Html . ActionLink ( " TEXTO PARA O USUÁRIO " , " ACTION " , new { Inicio = 0 , Final = 10 }) Código CSHTML 5.21: Acrescenta parâmetros de url www.k19.com.br 75
  • 86.
    C AMADA DEA PRESENTAÇÃO 76 BeginForm e EndForm Helpers No ASP.NET MVC, há um conjunto de HTML Helpers para facilitar a criação de formulários nas páginas de uma aplicação web. Para criar um formulário, utilizamos o helper BeginForm. Para terminar um formulário, utiliza- mos o helper EndForm. Veja o exemplo a seguir. 1 @ { Html . BeginForm () ;} 2 3 <! -- elementos do formulário -- > 4 5 @ { Html . EndForm () ;} Código CSHTML 5.22: Utilizando os helpers BeginForm e EndForm Também podemos utilizar o comando using para garantir que os formulários sejam fechados. Nesse caso, não é necessário adicionar o helper EndForm. Observe o formulário abaixo. 1 @using ( Html . BeginForm () ) { 2 3 <! -- elementos de formulário -- > 4 5 } Código CSHTML 5.23: Utilizando o comando using Por padrão, um formulário criado com o helper BeginForm enviará requisições à ação associada à url atual. Contudo, podemos definir explicitamente uma outra ação para receber essas requisições. Veja um exemplo no código abaixo. 1 @using ( Html . BeginForm ( " ACTION " , " CONTROLADOR " ) ) { 2 3 <! -- elementos de formulário -- > 4 5 } Código CSHTML 5.24: Definindo uma ação e um controlador explicitamente Por padrão, os formulários gerados pelo helper BeginForm fazem requisições do tipo POST. Nesse caso, as ações associadas a esses formulários devem ser anotadas com HttpPost para indicar o tipo de requisição HTTP esperado. 1 public class K19Controller : Controller 2 { 3 // POST : / K19 / Home 4 [ HttpPost ] 5 public ActionResult Home () 6 { 7 ... 8 return View () ; 9 } 10 } Código C# 5.6: Anotando uma ação com HttpPost 76 www.k19.com.br
  • 87.
    77 C AMADA DE A PRESENTAÇÃO CheckBox Podemos adicionar um checkbox em um formulário através do helper CheckBox. 1 @Html . CheckBox ( " Aceito " , false ) Código CSHTML 5.25: Utilizando o helper CheckBox O código acima produz o seguinte trecho de código HTML: 1 < input id = " Aceito " name = " Aceito " type = " checkbox " value = " true " / > 2 < input name = " Aceito " type = " hidden " value = " false " / > Código HTML 5.3: HTML gerado pelo helper CheckBox Considere o formulário a seguir: 1 @using ( Html . BeginForm ( " Cadastra " , " Contrato " ) ) 2 { 3 @Html . CheckBox ( " Aceito " , false ) 4 < input type = " submit " value = " Cadastra Contrato " / > 5 } Código CSHTML 5.26: Formulário Agora, considere a ação associada ao formulário criado no código acima. 1 public class ContratoController : Controller 2 { 3 // POST : / Contrato / Cadastra 4 [ HttpPost ] 5 public ActionResult Cadastra ( Contrato contrato ) 6 { 7 ... 8 return View () ; 9 } 10 } Código C# 5.7: Ação O valor enviado através do checkbox inserido no formulário será armazenado na propriedade Aceito do contrato enviado como parâmetro para a ação Cadastra(). Dessa forma, a classe Contrato deve possuir uma propriedade booleana chamada Aceito. 1 public class Contrato 2 { 3 public Boolean Aceito { get ; set ; } 4 } Código C# 5.8: Contrato.cs TextBox Uma caixa de texto pode ser adicionada através do helper TextBox. 1 @Html . TextBox ( " Nome " , " Digite o seu nome " ) www.k19.com.br 77
  • 88.
    C AMADA DEA PRESENTAÇÃO 78 Código CSHTML 5.27: Utilizando o helper TextBox O código acima produz o seguinte trecho de código HTML: 1 < input id = " Nome " name = " Nome " type = " text " value = " Digite o seu nome " / > Código HTML 5.4: HTML gerado pelo helper TextBox TextArea Uma caixa para textos maiores pode ser adicionada através do helper TextArea. 1 @Html . TextArea ( " Mensagem " , " Digite uma mensagem " ) Código CSHTML 5.28: Utilizando o helper TextArea O código acima produz o seguinte trecho de código HTML: 1 < textarea cols = " 20 " id = " Mensagem " name = " Mensagem " rows = " 2 " > Digite uma mensagem </ ← textarea > Código HTML 5.5: HTML gerado pelo helper TextArea RadioButton Um botão do tipo radio pode ser adicionado através do helper RadioButton. 1 @Html . RadioButton ( " CidadeNatal " , " São Paulo " , true ) 2 @Html . RadioButton ( " CidadeNatal " , " Natal " , false ) 3 @Html . RadioButton ( " CidadeNatal " , " Piracicaba " , false ) Código CSHTML 5.29: Utilizando o helper RadioButton O código acima produz o seguinte trecho de código HTML: 1 < input checked = " checked " id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " São ← Paulo " / > 2 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Natal " / > 3 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Piracicaba " / > Código HTML 5.6: HTML gerado pelo helper RadioButton Hidden Um campo escondido pode ser adicionado através do helper Hidden. 1 @Html . Hidden ( " Id " , 1) Código CSHTML 5.30: Utilizando o helper Hidden 78 www.k19.com.br
  • 89.
    79 C AMADA DE A PRESENTAÇÃO O código acima produz o seguinte trecho de código HTML: 1 < input id = " Id " name = " Id " type = " hidden " value = " 1 " / > Código HTML 5.7: HTML gerado pelo helper Hidden Password Uma caixa para senha pode ser adicionada através do helper Password. 1 @Html . Password ( " Password " , " senha " ) Código CSHTML 5.31: Utilizando o helper Password O código acima produz o seguinte trecho de código HTML: 1 < input id = " Password " name = " Password " type = " password " value = " senha " / > Código HTML 5.8: HTML gerado pelo helper Password Mais Sobre Os dados preenchidos nos elementos de um formulário são recuperados da ViewBag. Considere o código abaixo: 1 @Html . TextBox ( " Nome " ) Código CSHTML 5.32: Utilizando o helper TextBox Caso exista uma chave chamada Nome na ViewBag, então o valor associado a essa chave será o valor inicial da caixa de texto. Podemos alterar esse comportamento, passando um segundo parâmetro para o helper TextBox, que será o valor inicial da caixa de texto. Observe o código a seguir. 1 @Html . TextBox ( " Nome " , " NomeInicial " ) Código CSHTML 5.33: Utilizando o helper TextBox Strongly Typed Views Se os HTML Helpers forem utilizados de maneira análoga à mostrada anteriormente, a probabi- lidade de ocorrer um erro de digitação é alta. A forma que os HTML Helpers foram aplicados não permite que o compilador verifique a existência das propriedades associadas aos elementos dos for- mulários. Por exemplo, considere o código a seguir. 1 @Html . CheckBox ( " Aceito " , false ) Código CSHTML 5.34: Utilizando o helper CheckBox www.k19.com.br 79
  • 90.
    C AMADA DEA PRESENTAÇÃO 80 O código anterior supõe que o objeto recebido como parâmetro pela ação associada ao formulá- rio onde o checkbox foi inserido possua uma propriedade chamada Aceito. Essa propriedade pode não existir. Contudo, nenhum erro de compilação seria gerado nesse caso. Para evitar esse tipo de problema, podemos utilizar a seguinte sintaxe em telas fortemente tipa- das: 1 @model K19 . Models . Contrato 2 ... 3 @Html . CheckBoxFor ( c = > c . Aceito ) 4 ... Código CSHTML 5.35: Utilizando o helper CheckBoxFor Com essa sintaxe, o compilador tem condições de verificar a existência das propriedades. Sendo assim, ele produzirá um erro de compilação se uma propriedade inexistente for utilizada. TextBoxFor 1 @model K19 . Models . Empresa 2 ... 3 @Html . TextBoxFor ( x = > x . Nome ) 4 ... Código CSHTML 5.36: Utilizando o helper TextBoxFor O código acima produz o seguinte trecho de código HTML: 1 < input id = " Nome " name = " Nome " type = " text " value = " K19 Treinamentos " / > Código HTML 5.9: HTML gerado pelo helper TextBoxFor TextAreaFor 1 @model K19 . Models . Empresa 2 ... 3 @Html . TextAreaFor ( x = > x . Descricao ) 4 ... Código CSHTML 5.37: Utilizando o helper TextAreaFor O código acima produz o seguinte trecho de código HTML: 1 < textarea cols = " 20 " id = " Descricao " name = " Descricao " rows = " 2 " > 2 K19 Treinamentos em Java e . NET 3 </ textarea > Código HTML 5.10: HTML gerado pelo helper TextAreaFor RadioButtonFor 80 www.k19.com.br
  • 91.
    81 C AMADA DE A PRESENTAÇÃO 1 @model K19 . Models . Pessoa 2 ... 3 @Html . RadioButtonFor ( x = > x . CidadeNatal , " São Paulo " ) 4 @Html . RadioButtonFor ( x = > x . CidadeNatal , " Natal " ) 5 @Html . RadioButtonFor ( x = > x . CidadeNatal , " Piracicaba " ) 6 ... Código CSHTML 5.38: Utilizando o helper RadioButtonFor O código acima produz o seguinte trecho de código HTML: 1 < input checked = " checked " id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " São ← Paulo " / > 2 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Natal " / > 3 < input id = " CidadeNatal " name = " CidadeNatal " type = " radio " value = " Piracicaba " / > Código HTML 5.11: HTML gerado pelo helper RadioButtonFor HiddenFor 1 @model K19 . Models . Empresa 2 ... 3 @Html . HiddenFor ( e = > e . Id ) 4 ... Código CSHTML 5.39: Utilizando o helper HiddenFor O código acima produz o seguinte trecho de código HTML: 1 < input id = " Id " name = " Id " type = " hidden " value = " 1 " / > Código HTML 5.12: HTML gerado pelo helper HiddenFor PasswordFor 1 @model K19 . Models . Usuario 2 ... 3 @Html . PasswordFor ( x = > x . Senha ) 4 ... Código CSHTML 5.40: Utilizando o helper PasswordFor O código acima produz o seguinte trecho de código HTML: 1 < input id = " Senha " name = " Senha " type = " password " / > Código HTML 5.13: HTML gerado pelo helper PasswordFor Mais Sobre Os dados preenchidos nos elementos de um formulário de uma tela fortemente tipada são recuperados da propriedade Model. Considere o código abaixo: www.k19.com.br 81
  • 92.
    C AMADA DEA PRESENTAÇÃO 82 1 @model K19 . Models . Pessoa 2 ... 3 @Html . TextBoxFor ( x = > x . Nome ) 4 ... Código CSHTML 5.41: Utilizando o helper TextBoxFor Caso exista a propriedade Model.Nome, o valor inicial dessa caixa de texto será o valor dessa propriedade. DropDownList Helper Considere uma aplicação para cadastrar livros de uma biblioteca. Quando um livro é cadastro, o usuário podem escolher a editora desse livro. A editora pode ser selecionada através de um drop down list. Para criar um drop down list, podemos utilizar o HTML Helper DropDownList.cshtml O primeiro passo para utilizar o helper DropDownList é criar uma SelectList na camada de controle com as opções que o usuário poderá selecionar. 1 public class LivroController : Controller 2 { 3 public ActionResult Create () 4 { 5 List < Editora > editoras = editoraRepository . BuscaTodas () ; 6 ViewBag . Editoras = new SelectList ( editoras , " Id " ," Nome " ) ; 7 return View () ; 8 } 9 } Código C# 5.9: LivroController.cs O segundo passo é adicionar o helper DropDownList na camada de apresentação. 1 @Html . DropDownList ( " Editoras " ) Código CSHTML 5.42: Utilizando o helper DropDownList EditorFor Considere a seguinte entidade. 1 public class Editora 2 { 3 public int Id { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public bool IsAtivo { get ; set ; } 7 public string Descricao { get ; set ; } 8 public virtual ICollection < Livro > Livros { get ; set ; } 9 } Código C# 5.10: Editora.cs 82 www.k19.com.br
  • 93.
    83 C AMADA DE A PRESENTAÇÃO Para editar os dados de uma editora, temos uma página conforme o exemplo abaixo: 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Edição de Editora " ; 5 } 6 7 < h2 > Edição de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . HiddenFor ( model = > model . Id ) 11 12 @Html . LabelFor ( model = > model . Nome ) 13 @Html . TextBoxFor ( model = > model . Nome ) 14 15 < br / > 16 17 @Html . LabelFor ( model = > model . Email ) 18 @Html . TextBoxFor ( model = > model . Email ) 19 20 < br / > 21 22 @Html . LabelFor ( model = > model . Descricao ) 23 @Html . TextAreaFor ( model = > model . Descricao ) 24 25 < br / > 26 27 @Html . LabelFor ( model = > model . IsAtivo ) 28 @Html . CheckBoxFor ( model = > model . IsAtivo ) 29 30 < input type = " submit " value = " Salvar " / > 31 } Código CSHTML 5.43: Edit.cshtml Para cada propriedade da entidade Editora, utilizamos um helper para gerar o código HTML necessário para a entrada de dados. Por exemplo, no caso das propriedades Nome e Email, utilizamos o Helper TextBox. Para a propriedade booleana IsAtivo, utilizamos o helper CheckBox. Observe que a escolha do helper depende basicamente do tipo da propriedade associada a ele. Podemos deixar essa escolha a cargo do helper EditorFor. Para propriedades de tipo booleano, esse helper utilizará o CheckBox. Para propriedades do tipo string, esse helper utilizará o TextBox. Observe a utilização do helper EditorFor no código abaixo. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Edição de Editora " ; 5 } 6 7 < h2 > Edição de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . HiddenFor ( model = > model . Id ) 11 12 @Html . LabelFor ( model = > model . Nome ) 13 @Html . EditorFor ( model = > model . Nome ) 14 15 < br / > 16 17 @Html . LabelFor ( model = > model . Email ) 18 @Html . EditorFor ( model = > model . Email ) 19 20 < br / > 21 www.k19.com.br 83
  • 94.
    C AMADA DEA PRESENTAÇÃO 84 22 @Html . LabelFor ( model = > model . Descricao ) 23 @Html . EditorFor ( model = > model . Descricao ) 24 25 < br / > 26 27 @Html . LabelFor ( model = > model . IsAtivo ) 28 @Html . EditorFor ( model = > model . IsAtivo ) 29 30 < input type = " submit " value = " Salvar " / > 31 } Código CSHTML 5.44: Edit.cshtml Para personalizar o funcionamento do helper EditorFor, podemos utilizar algumas anotações na entidade Editora. Por exemplo, seria mais apropriado utilizar um TextAreaFor para editar a descrição de uma editora ao invés de um TextBoxFor. Contudo, como dito anteriormente, o helper EditorFor utilizará um TextBoxFor para a propriedade Descricao. Nesse caso, podemos aplicar a anotação MultilineText na propriedade Descricao. 1 public class Editora 2 { 3 public int Id { get ; set ; } 4 public string Nome { get ; set ; } 5 public string Email { get ; set ; } 6 public bool IsAtivo { get ; set ; } 7 [ MultilineText ] 8 public string Descricao { get ; set ; } 9 public virtual ICollection < Livro > Livros { get ; set ; } 10 } Código C# 5.11: Editora.cs Além da anotação MultilineText, o ASP.NET MVC define as seguintes anotações para persona- lizar o helper EditorFor. Anotação Descrição HiddenInput Utiliza o Helper Hidden Text Utiliza o Helper Text String Utiliza o Helper TextBox Password Utiliza o Helper Password MultilineText Utiliza o Helper TextArea Boolean Utiliza o Helper CheckBox ou DropDownList para nullable boolean Decimal Utiliza o Helper TextBox e formata para duas casas decimais Object Percorre as propriedades do objeto e define o Helper adequado para cada uma Collection Percorre através do IEnumerable e define o Helper para cada elemento EditorForModel Também temos o helper EditorForModel para construir um formulário completo com base nas propriedades de uma entidade. Esse helper seguirá a mesma abordagem do EditorFor. Ou seja, para cada propriedade do Model, ele utilizará o helper apropriado. 1 @model K19 . Models . Editora 2 3 @{ 84 www.k19.com.br
  • 95.
    85 C AMADA DE A PRESENTAÇÃO 4 ViewBag . Title = " Edição de Editora " ; 5 } 6 7 < h2 > Edição de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . EditorForModel () 11 12 < input type = " submit " value = " Save " / > 13 } Código CSHTML 5.45: Edit.cshtml Exercícios de Fixação 6 No projeto K19, defina uma página com um formulário de cadastro de editoras. Os campos nome e email deste formulário devem vir preenchidos com os valores dos atributos de um objeto da classe Editora. O nome e email deverão ser acessados através da propriedade ViewBag. Acrescente a ação TestaFormularioHtml no controlador Teste. 1 public ActionResult TestaFormularioHtml () 2 { 3 Editora e = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 ViewBag . Nome = e . Nome ; 5 ViewBag . Email = e . Email ; 6 return View () ; 7 } Código C# 5.12: Action TestaFormularioHtml do TesteController Crie a página TestaFormularioHtml.cshtml dentro da pasta Teste que fica na pasta Views. 1 @{ 2 ViewBag . Title = " TestaFormularioHtml " ; 3 } 4 < h2 > Cadastro de Editora </ h2 > 5 < form > 6 < label for = " Nome " > Nome : </ label > 7 < input id = " Nome " name = " Nome " value = " @ViewBag . Nome " / > 8 9 < label for = " Email " > Email : </ label > 10 < input id = " Email " name = " Email " value = " @ViewBag . Email " / > 11 12 < input type = " submit " value = " Enviar " / > 13 </ form > Código CSHTML 5.46: TestaFormularioHtml.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml. 7 Crie um formulário de cadastro de editoras conforme o exercício anterior, mas utilize Html Helpers. Altere o controlador Teste e acrescente a ação TestaHelpers. 1 public ActionResult TestaHelpers () 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View () ; 5 } www.k19.com.br 85
  • 96.
    C AMADA DEA PRESENTAÇÃO 86 Código C# 5.13: Ação TestaHelpers do TesteController Crie a página TestaHelpers.cshtml dentro da pasta Teste que fica na pasta Views. 1 @{ 2 @ViewBag . Title = " TestaHelpers " ; 3 } 4 5 < h2 > Cadastro de Editora usando Helpers </ h2 > 6 @using ( Html . BeginForm () ) 7 { 8 @Html . Label ( " Nome " ) 9 @Html . TextBox ( " Nome " ) 10 @Html . Label ( " Email " ) 11 @Html . TextBox ( " Email " ) 12 < input type = " submit " value = " Enviar " / > 13 } Código CSHTML 5.47: TestaHelpers.cshtml Para testar, acesse acesse a url http://localhost:<PORTA_APP>/Teste/TestaHelpers. 8 Crie um novo formulário de cadastro de editoras conforme o exercício anterior, mas utilize HTML Helpers Strongly Typed. Altere o controlador Teste e acrescente a ação TestaHelpersTipados. 1 public ActionResult TestaHelpersTipados () 2 { 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View ( editora ) ; 5 } Código C# 5.14: Action TestaHelpersTipados do TesteController Crie a página TestaHelpersTipados.cshtml dentro da pasta Teste que fica na pasta Views. 1 @model K19 . Models . Editora 2 3 @{ 4 @ViewBag . Title = " TestaHelpersTipados " ; 5 } 6 7 < h2 > Cadastro de editoras usando HTML Helpers Strongly Typed </ h2 > 8 @using ( Html . BeginForm () ) 9 { 10 @Html . LabelFor ( x = > x . Nome ) 11 @Html . TextBoxFor ( x = > x . Nome ) 12 @Html . LabelFor ( x = > x . Email ) 13 @Html . TextBoxFor ( x = > x . Email ) 14 < input type = " submit " value = " Enviar " / > 15 } Código CSHTML 5.48: TestaHelpersTipados.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Teste/TestaHelpersTipados. 9 Conforme os exercícios anteriores, crie um formulário de cadastro de editoras utilizando o HTML Helper EditorFor. Altere o controlador Teste e acrescente a seguinte ação. 1 public ActionResult TestaEditorFor () 2 { 86 www.k19.com.br
  • 97.
    87 C AMADA DE A PRESENTAÇÃO 3 Editora editora = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 4 return View ( editora ) ; 5 } Código C# 5.15: Ação TestaEditorFor do TesteController Crie a página TestaEditorFor.cshtml dentro da pasta Teste que fica na pasta Views. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " TestaEditorFor " ; 5 } 6 7 < h2 > Cadastro de editoras usando EditorFor </ h2 > 8 @using ( Html . BeginForm () ) 9 { 10 @Html . LabelFor ( x = > x . Nome ) 11 @Html . EditorFor ( x = > x . Nome ) 12 @Html . LabelFor ( x = > x . Email ) 13 @Html . EditorFor ( x = > x . Email ) 14 < input type = " submit " value = " Enviar " / > 15 } Código HTML 5.14: TestaEditorFor.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Teste/TestaEditorFor. Layouts É comum que as páginas de uma aplicação web possuam conteúdo em comum (por exemplo, um cabeçalho ou um rodapé). O conteúdo em comum pode ser replicado em todas as páginas através do CTRL+C e CTRL+V. Porém, essa não é uma boa abordagem, pois quando alguma alteração precisa ser realizada, todos os arquivos devem ser modificados. Também é comum que as páginas de uma aplicação web possuam um certo padrão visual. Daí surge o conceito de Layouts. Conteúdo comum Tudo que é comum a todas as páginas de um determinado grupo pode ser definido em um Layout. Dessa forma, qualquer alteração é facilmente realizada modificando-se apenas um arquivo. Por exemplo, suponha que toda página de uma aplicação web deva ter o mesmo título e a mesma formatação. Podemos criar um Layout com o título desejado e com a referência ao arquivo CSS que define a formatação padrão. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < title > @ViewBag . Title </ title > 5 @Styles . Render ( " ~/ Content / Site . css " ) 6 @Scripts . Render ( " ~/ Scripts / jquery -1.5.1. min . js " ) 7 </ head > 8 9 < body > 10 < div id = " header " > 11 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 12 @Html . ActionLink ( " Livros " , " Index " ," Livro " ) www.k19.com.br 87
  • 98.
    C AMADA DEA PRESENTAÇÃO 88 13 </ div > 14 @RenderBody () 15 </ body > 16 </ html > Código CSHTML 5.49: K19Layout.cshtml No layout definido no código acima, utilizamos o método Render() das propriedades Styles e Scripts para adicionar conteúdo CSS e JavaScript. Na tag <title>, acrescentamos @ViewBag.Title, para que cada página possa definir o seu próprio título. Por fim, o método RenderBody() indica onde o conteúdo definido em cada página será adicionado. É recomendado que os arquivos que definam layouts sejam colocados na pasta ViewsShare. Mais Sobre Suponha que devemos adicionar diversos arquivos CSS à uma página de uma aplicação ASP.NET MVC. Se esses arquivos forem adicionados um a um da maneira tradicional (com a tag <link>), o número de requisições que os navegadores terão de realizar para carre- gar essa página será alto, afetando o desempenho. Para contornar esse problema, poderíamos agrupar o código CSS de todos os arquivos em apenas um. Contudo, essa abordagem dificultaria a manutenção do código CSS. Dessa forma, o ASP.NET MVC 4 possui um mecanismo capaz de agrupar o conteúdo CSS de diversos arquivos dinamicamente. Para usar esse mecanismo, basta invocar o método Render() da propriedade Styles. 1 @Styles . Render ( " ~/ Content / Arquivo1 . css " , " ~/ Content / Arquivo2 . css " , " ~/ Content / Arquivo3 ← . css " ) Código CSHTML 5.50: Utilizando o método Render para adicionar conteúdo CSS Analogamente, a propriedade Scripts possui o método Render(), que também tem por obje- tivo agrupar o código JavaScript de diversos arquivos. Com o layout definido, o próximo passo é indicar quais páginas utilizarão esse layout. Por exem- plo, podemos atualizar a página de edição de editoras para utilizar K19Layout.cshtml como layout. 1 @model K19 . Models . Editora 2 3 @{ 4 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 5 6 // Define o título específico desta página 7 ViewBag . Title = " Edição de Editora " ; 8 } 9 10 < h2 > Edição de Editora </ h2 > 11 12 @using ( Html . BeginForm () ) { 13 @Html . EditorForModel () 14 < input type = " submit " value = " Salvar " / > 15 } Código CSHTML 5.51: Edit.cshtml Quando a página de edição de editoras é requisitada, o arquivo Edit.cshtml é processado antes do arquivo K19Layout.cshtml, o que permite definir o valor de ViewBag.Title no arquivo Edit.cshtml 88 www.k19.com.br
  • 99.
    89 C AMADA DE A PRESENTAÇÃO e utilizá-lo no layout. Para definir o layout de Edit.cshtml, foi necessário armazenar o caminho completo do arquivo que define o layout na propriedade Layout. Este procedimento não é muito prático, pois em cada página que deseja utilizar esse layout é necessário definir esta propriedade. A partir da terceira versão do ASP.NET MVC, temos uma nova funcionalidade que permite definir um layout padrão para todas as páginas, não havendo necessidade de definir a propriedade Layout em cada página. Para isso, basta acrescentarmos o arquivo _ViewStart.cshtml à pasta View: O _ViewStart.cshtml permite definirmos um código que será executado antes de cada página ser renderizada. Nesse arquivo podemos definir, por exemplo, a propriedade Layout: 1 @{ 2 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 3 } Código CSHTML 5.52: _ViewStart.cshtml Como este código é processado antes dos arquivos específicos de cada página, não há mais ne- cessidade de definir a propriedade Layout em cada arquivo específico. Lacunas Também podemos criar seções em um layout para serem preenchidas com conteúdos específicos definidos nas páginas. Considere o seguinte layout. 1 <! DOCTYPE html > www.k19.com.br 89
  • 100.
    C AMADA DEA PRESENTAÇÃO 90 2 < html > 3 < head > 4 < title > @ViewBag . Title </ title > 5 @Styles . Render ( " ~/ Content / Site . css " ) 6 @Scripts . Render ( " ~/ Scripts / jquery -1.5.1. min . js " ) 7 </ head > 8 9 < body > 10 < div id = " header " > 11 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 12 @Html . ActionLink ( " Livros " , " Index " ," Livro " ) 13 </ div > 14 < div id = " sidebar " > @RenderSection ( " Sidebar " , false ) </ div > 15 < div id = " content " > @RenderBody () </ div > 16 < div id = " footer " > K19 Treinamentos </ div > 17 </ body > 18 </ html > Código CSHTML 5.53: K19Layout.cshtml No código acima, utilizamos o método @RenderSection() para criar uma seção no layout. O primeiro parâmetro (“Sidebar”) é o nome da seção e o segundo é um booleano que indica se o pre- enchimento dessa seção é obrigatório ou não. Para definir o conteúdo de uma seção, devemos utilizar o código @section. Observe o código de uma página que utiliza o layout criado anteriormente e define a seção Sidebar. 1 @model K19 . Models . Editora 2 3 @{ 4 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 5 6 // Define o título específico desta página 7 ViewBag . Title = " Edição de Editora " ; 8 } 9 10 < h2 > Edição de Editora </ h2 > 11 12 @using ( Html . BeginForm () ) { 13 @Html . EditorForModel () 14 < input type = " submit " value = " Salvar " / > 15 } 16 17 @section Sidebar { 18 <p > Sidebar do cadastro de Edição de Editora </ p > 19 } Código CSHTML 5.54: Edit.cshtml Exercícios de Fixação 10 Crie um arquivo chamado K19Layout.cshtml dentro da pasta Shared que fica na pasta Views do projeto K19. Este arquivo servirá de modelo para as páginas da nossa aplicação. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 90 www.k19.com.br
  • 101.
    91 C AMADA DE A PRESENTAÇÃO 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 14 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 15 16 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 17 18 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 19 20 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 21 </ div > 22 @RenderBody () 23 24 @Scripts . Render ( " ~/ bundles / jquery " ) 25 @RenderSection ( " scripts " , required : false ) 26 </ body > 27 </ html > Código CSHTML 5.55: K19Layout.cshtml 11 Altere a página TestaFormularioHtml.cshtml para utilizar a página de layout definida no exer- cício anterior. 1 @{ 2 ViewBag . Title = " TestaFormularioHtml " ; 3 } 4 5 @{ 6 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 7 } 8 9 < h2 > Cadastro de Editora </ h2 > 10 < form > 11 < label for = " Nome " > Nome : </ label > 12 < input id = " Nome " name = " Nome " value = " @ViewBag . Nome " / > 13 14 < label for = " Email " > Email : </ label > 15 < input id = " Email " name = " Email " value = " @ViewBag . Email " / > 16 17 < input type = " submit " value = " Enviar " / > 18 </ form > Código HTML 5.15: TestaFormularioHtml.cshtml Teste acessando o endereço: http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml. 12 Defina a página K19Layout.cshtml como layout padrão para todas as telas. Altere o arquivo _ViewStart.cshtml da pasta Views. 1 @{ 2 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 3 } Código CSHTML 5.56: _ViewStart.cshtml Teste acessando http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml e navegando pelos links. 13 Defina seções no layout K19Layout.cshtml. Primeiramente, acrescente ao arquivo Site.css da pasta Content o trecho de código a seguir: www.k19.com.br 91
  • 102.
    C AMADA DEA PRESENTAÇÃO 92 1 # sidebar 2 { 3 float : right ; 4 margin :10 px ; 5 padding :10 px ; 6 border : dotted 5 px red ; 7 width :180 px ; 8 } 9 10 # footer 11 { 12 text - align : center ; 13 clear : both ; 14 } 15 16 # content 17 { 18 float : left ; 19 margin :10 px ; 20 } 21 ... Código CSS 5.1: Site.css Finalmente, altere o arquivo K19Layout.cshtml da pasta ViewsShared. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 14 15 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 16 17 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 18 19 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 20 </ div > 21 < div id = " sidebar " > SideBar Padrão </ div > 22 < div id = " content " > @RenderBody () </ div > 23 < div id = " footer " > K19 Treinamentos </ div > 24 25 @Scripts . Render ( " ~/ bundles / jquery " ) 26 @RenderSection ( " scripts " , required : false ) 27 </ body > 28 </ html > Código CSHTML 5.57: K19Layout.cshtml Teste acessando http://localhost:<PORTA_APP>/Editora e navegando pelos links.. 14 Acrescente uma seção ao layout K19Layout.cshtml. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 92 www.k19.com.br
  • 103.
    93 C AMADA DE A PRESENTAÇÃO 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 14 15 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 16 17 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 18 19 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 20 </ div > 21 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 22 < div id = " content " > @RenderBody () </ div > 23 < div id = " footer " > K19 Treinamentos </ div > 24 25 @Scripts . Render ( " ~/ bundles / jquery " ) 26 @RenderSection ( " scripts " , required : false ) 27 </ body > 28 </ html > Código CSHTML 5.58: K19Layout.cshtml 15 Defina na página TestaFormularioHtml.cshtml a seção “Sidebar”. 1 @{ 2 ViewBag . Title = " TestaFormularioHtml " ; 3 } 4 5 @{ 6 Layout = " ~/ Views / Shared / K19Layout . cshtml " ; 7 } 8 9 < h2 > Cadastro de Editora </ h2 > 10 < form > 11 < label for = " Nome " > Nome : </ label > 12 < input id = " Nome " name = " Nome " value = " @ViewBag . Nome " / > 13 14 < label for = " Email " > Email : </ label > 15 < input id = " Email " name = " Email " value = " @ViewBag . Email " / > 16 17 < input type = " submit " value = " Enviar " / > 18 </ form > 19 20 @section Sidebar { 21 <p > Sidebar do TestaFormularioHtml </ p > 22 } Código CSHTML 5.59: TestaFormularioHtml.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Teste/TestaFormularioHtml e navegue pelos links.. 16 Acrescente uma seção padrão que será utilizada nas páginas que não definiram a seção “Side- bar”. 1 <! DOCTYPE html > 2 < html > 3 < head > 4 < meta charset = " utf -8 " / > 5 < meta name = " viewport " content = " width = device - width " / > 6 < title > @ViewBag . Title </ title > 7 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) www.k19.com.br 93
  • 104.
    C AMADA DEA PRESENTAÇÃO 94 8 @Scripts . Render ( " ~/ bundles / modernizr " ) 9 </ head > 10 < body > 11 < div id = " header " > 12 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 13 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 14 15 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 16 17 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 18 19 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 20 </ div > 21 @if ( IsSectionDefined ( " Sidebar " ) ) 22 { 23 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 24 } 25 else 26 { 27 < div id = " sidebar " > Sidebar padrão </ div > 28 } 29 < div id = " content " > @RenderBody () </ div > 30 < div id = " footer " > K19 Treinamentos </ div > 31 32 @Scripts . Render ( " ~/ bundles / jquery " ) 33 @RenderSection ( " scripts " , required : false ) 34 </ body > 35 </ html > Código CSHTML 5.60: K19Layout.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Editora e navegue pelos links.. Partial views Quanto mais elaborada é uma página de uma aplicação web, mais extenso é o seu código. Códi- gos muito extensos prejudicam a legibilidade e a manutenção da aplicação. Para organizar melhor o código, podemos dividir o conteúdo de uma página web em vários arquivos. Suponha que desejamos dividir o conteúdo de uma página em duas partes. Devemos criar um arquivo para cada parte. 1 < h1 > Parte 1 </ h > 2 <p > Conteúdo da parte1 </ p > Código CSHTML 5.61: _Parte1.cshtml 1 < h1 > Parte 2 </ h > 2 <p > Conteúdo da parte2 </ p > Código CSHTML 5.62: _Parte2.cshtml Mais Sobre Por convenção, o nome dos arquivos que definem as telas parciais de uma aplicação ASP.NET MVC devem iniciar com o caractere “_”. 94 www.k19.com.br
  • 105.
    95 C AMADA DE A PRESENTAÇÃO Por fim, devemos criar um arquivo principal para agrupar essas partes. Utilizaremos o método Partial() para inserir o conteúdo dos arquivos secundários no arquivo principal. 1 < html > 2 < head > 3 < title > Exemplo de partial </ title > 4 </ head > 5 6 < body > 7 Html . Partial ( " _Parte1 " ) 8 Html . Partial ( " _Parte2 " ) 9 </ body > 10 </ html > Código CSHTML 5.63: Principal.cshtml O método Partial() procura os arquivos _Parte1.cshtml e _Parte2.cshtml no mesmo dire- tório do arquivo principal. Caso ele não encontre, ele continua procurando esses arquivos na pasta ViewsShared. Essa lógica também é aplicada no método View que utilizamos na camada de con- trole. O recurso de páginas parciais permite a criação de conteúdo reutilizável de forma mais clara e concisa. Informações podem ser compartilhadas entre as páginas principais e as páginas parciais através da propriedade ViewBag. Por exemplo, podemos definir duas páginas principais, uma para criar e outra para editar editoras. Nessas duas páginas podemos acrescentar uma página parcial com o formulário que pode ser utilizado para cadastrar novas editoras ou editar editoras existentes. Veja abaixo o exemplo da página parcial que contém o formulário de cadastro ou edição de edi- toras. 1 <! -- ~/ Views / Editora / _Form . cshtml -- > 2 @model K19 . Models . Editora 3 4 @Scripts . Render ( 5 " ~/ Scripts / jquery -1.5.1. min . js " , 6 " ~/ Scripts / jquery . validate . min . js " , 7 " ~/ Scripts / jquery . validate . unobtrusive . min . js " ) 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 < fieldset > 12 < legend > Editora </ legend > 13 @if ( Model != null ) 14 { 15 @Html . EditorFor ( model = > model . Id ) 16 } 17 18 < div class = " editor - label " > 19 @Html . LabelFor ( model = > model . Nome ) 20 </ div > 21 < div class = " editor - field " > 22 @Html . EditorFor ( model = > model . Nome ) 23 </ div > 24 25 < div class = " editor - label " > 26 @Html . LabelFor ( model = > model . Email ) 27 </ div > 28 < div class = " editor - field " > 29 @Html . EditorFor ( model = > model . Email ) 30 </ div > 31 32 < div class = " editor - label " > 33 @Html . LabelFor ( model = > model . IsAtivo ) www.k19.com.br 95
  • 106.
    C AMADA DEA PRESENTAÇÃO 96 34 </ div > 35 < div class = " editor - field " > 36 @Html . EditorFor ( model = > model . IsAtivo ) 37 </ div > 38 39 <p > 40 < input type = " submit " value = " Save " / > 41 </ p > 42 </ fieldset > 43 } Código CSHTML 5.64: _Form.cshtml Veja abaixo o exemplo das páginas principais que utilizam a página parcial definida no código acima. 1 <! -- ~/ Views / Editora / Create . cshtml -- > 2 @model K19 . Models . Editora 3 4 @{ 5 ViewBag . Title = " Cadastro de Editora " ; 6 } 7 8 < h2 > Cadastro de Editora </ h2 > 9 10 @Html . Partial ( " _Form " ) Código CSHTML 5.65: Create.cshtml 1 <! -- ~/ Views / Editora / Edit . cshtml -- > 2 @model LivrariaVirtual . Models . Editora 3 4 @{ 5 ViewBag . Title = " Edição de Editora " ; 6 } 7 8 < h2 > Edição de Editora </ h2 > 9 10 @Html . Partial ( " _Form " ) Código CSHTML 5.66: Edit.cshtml Exercícios de Fixação 17 Crie uma página principal composta por outras páginas parciais. Para isto, altere o controlador Teste do projeto K19 e acrescente a ação chamada TestaPartial. 1 ... 2 public ActionResult TestaPartial () 3 { 4 return View () ; 5 } 6 ... Código C# 5.16: Nova ação TestaPartial do controlador Teste 18 Defina duas páginas parciais. Crie os arquivos _Parcial1.cshtml e _Parcial2.cshtml na pasta Teste que fica na pasta Views do projeto K19. 96 www.k19.com.br
  • 107.
    97 C AMADA DE A PRESENTAÇÃO 1 < h2 > Parcial1 </ h2 > Código CSHTML 5.67: _Parcial1.cshtml 1 < h2 > Parcial2 </ h2 > Código CSHTML 5.68: _Parcial2.cshtml 19 Crie uma página principal chamada TestaPartial.cshtml na pasta Teste que fica na pasta Views do projeto K19 e adicione as duas páginas parciais criadas anteriormente. 1 @{ 2 ViewBag . Title = " TestaPartial " ; 3 } 4 5 < h2 > Testando Partial </ h2 > 6 7 @Html . Partial ( " _Parcial1 " ) 8 @Html . Partial ( " _Parcial2 " ) Código CSHTML 5.69: TestaPartial.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Teste/TestaPartial www.k19.com.br 97
  • 108.
    C AMADA DEA PRESENTAÇÃO 98 98 www.k19.com.br
  • 109.
    CAPÍTULO CAMADA DE C ONTROLE 6 No ASP.NET MVC as urls são mapeadas para métodos (ações) em classes que definem os chama- das controladores. As requisições enviadas pelos navegadores são processadas pelos controlado- res. O processamento realizado por um controlador para tratar uma requisição consiste basicamente em: • Recuperar os dados enviados pelo usuário através de formulários. • Interagir com a camada de modelo. • Acionar a camada de apresentação para construir a página HTML que deve ser enviada para o usuário como resposta à sua requisição. Para que uma classe seja considerada um controlador, ela deve seguir algumas regras. • O nome da classe deve ter o sufixo “Controller”. • A classe deve implementar a interface System.Web.Mvc.IController ou herdar da classe Sys- tem.Web.Mvc.Controller. Raramente, você definirá uma classe para criar um controlador implementando a interface ICon- troller. Comumente as classes que definem um controlador derivam de Controller. Diversas propriedade e métodos são herdados da classe Controller. Essas propriedades facilitam o proces- samento das requisições e a utilização dos recursos do ASP.NET MVC. Actions Os controladores e as ações são elementos fundamentais de uma aplicação ASP.NET MVC. Um controlador pode conter diversas ações. As ações são utilizadas para processar as requisições realiza- das pelos navegadores. Para criar uma ação, é necessário definir um método public dentro da classe de um controlador. Os parâmetros desse método podem ser utilizados para receber os dados envia- dos pelos usuários através de formulários HTML. Esse método deve devolver um ActionResult que será utilizado pelo ASP.NET MVC para definir o que deve ser executado depois que a ação terminar. Quando um usuário faz uma requisição HTTP através de um navegador, o ASP.NET MVC verifica na tabela de rotas o controlador e a ação associados à url da requisição realizada. Essa tabela é definida no arquivo RouteConfig.cs e inicializada no arquivo Global.asax. Por padrão, quando criamos um projeto ASP.NET MVC no Visual Web Developer, uma rota com o formato {controller}/{action}/{id} é adicionada na tabela de rotas. Com essa rota, se uma www.k19.com.br 99
  • 110.
    C AMADA DEC ONTROLE 100 requisição for realizada para a url http://www.k19.com.br/Editora/Listagem, o controlador defi- nido pela classe EditoraController e a ação implementada pelo método Listagem() dessa classe serão escolhidos para processar essa requisição. Se uma requisição for realizada para a url http://www.k19.com.br/Editora/Remove/1, o con- trolador definido pela classe EditoraController e a ação implementada pelo método Remove() dessa classe serão escolhidos para processar a requisição realizada. Além disso, o valor 1 será pas- sado como parâmetro para o método Remove(). Veremos mais sobre rotas no Capítulo 7. ActionResult Quando uma ação termina, o método correspondente deve devolver um ActionResult. O valor devolvido indica para o ASP .NET MVC o que deve ser executado depois da ação. Veja abaixo uma lista com alguns tipos específicos de ActionResult que podem ser utilizados. ViewResult: Devolve uma página da camada de apresentação. Considere o seguinte exemplo. 1 public class TesteController 2 { 3 public ActionResult Acao () 4 { 5 return View () ; 6 } 7 } Considerando uma aplicação ASP .NET MVC 4 em C# e Razor, o método View(), ao ser cha- mado sem parâmetros, executará o seguinte processo para determinar qual arquivo deve ser utilizado para construir a página de resposta. 1. Se o arquivo ViewsTesteAcao.cshtml existir, ele será utilizado. 2. Caso contrário, se o arquivo ViewsSharedAcao.cshtml existir, ele será utilizado. 3. Se nenhum desses arquivos existir, uma página de erro será devolvida. Por outro lado, podemos especificar o nome do arquivo que define a página de resposta. Veja o exemplo abaixo. 1 return View ( " NomeDaView " ) ; Além disso, podemos passar um objeto para a camada de apresentação. Esse objeto será utili- zado nas páginas fortemente tipadas como vimos no Capítulo 5. 1 return View ( editora ) ; Também é possível especificar o nome do arquivo que define a página de resposta e o objeto que deve ser transmitido para a camada de apresentação ao mesmo tempo. 1 return View ( " NomeDaView " , editora ) ; PartialViewResult: Devolve uma página parcial da camada de apresentação. Exemplos: 100 www.k19.com.br
  • 111.
    101 C AMADA DE C ONTROLE 1 return PartialView () ; O método PartialView() utiliza a mesma abordagem do método View() para determinar o arquivo que deve ser utilizado para construir a página parcial de resposta. 1 return PartialView ( " NomeDaPartialView " , editora ) ; RedirectResult: Redireciona o navegador para uma URL específica. Exemplo: 1 return Redirect ( " http :// www . k19 . com . br " ) ; RedirectToAction: Redireciona para outra ação da camada de controle. Exemplo: 1 return RedirectToAction ( " OutraAction " , " OutroController " ) ; ContentResult: Devolve texto. Exemplo: 1 return Content ( " Texto " ," text plain " ) ; JsonResult: Devolve um objeto no formato JSON. Exemplo: 1 return Json ( editora ) ; JavaScriptResult: Devolve código Javascript. Exemplo: 1 return JavaScript ( " $ ( ’# divResultText ’) . html ( ’ JavaScript Passed ’) ; " ) ; FileResult: Devolve dados binários. Exemplo: 1 return File ( @ " c : relatorio . pdf " , " application pdf " ) ; EmptyResult: Devolve uma resposta vazia. Exemplo: 1 return new EmptyResult () ; Parâmetros Os parâmetros enviados pelos usuários através de formulários HTML podem ser recuperados por meio da propriedade Request. 1 string nome = Request [ " Nome " ]; Código C# 6.14: Recuperando o parâmetro Nome enviado em uma requisição HTTP Esses parâmetros também podem ser recuperados através dos parâmetros da ação responsável pelo processamento da requisição HTTP realizada. Para isso, basta definir um parâmetro C# para cada parâmetro HTTP com o mesmo nome. Veja o exemplo abaixo: www.k19.com.br 101
  • 112.
    C AMADA DEC ONTROLE 102 1 < html > 2 < head > 3 < title > Cadastro de Editora </ title > 4 </ head > 5 < body > 6 < form action = " / Editoras / Salva " method = " post " > 7 Nome : < input type = " text " name = " nome " / > 8 Email : < input type = " text " name = " email " / > 9 < input type = " submit " / > 10 </ form > 11 </ body > 12 </ html > Código CSHTML 6.1: Cadastra.csthml No código acima, criamos um formulário HTML com os parâmetros nome e email. Esses parâ- metros serão recuperados na ação Salva definida no código abaixo. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( string nome , string email ) 4 { 5 Editora editora = new Editora { Nome = nome , Email = email }; 6 db . Editoras . Add ( editora ) ; 7 return View () ; 8 } 9 ... Código C# 6.15: EditoraController.cs O ASP.NET MVC também é capaz de montar objetos com os valores dos parâmetros HTTP envi- ados pelo usuário e passá-los como argumento para as ações dos controladores. 1 [ HttpPost ] 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 return View () ; 6 } Código C# 6.16: EditoraController.cs As propriedades dos objetos recebidos como argumentos nas ações dos controladores precisam ter os mesmos nomes dos parâmetros HTTP ignorando-se letras maiúsculas ou minúsculas. Exercícios de Fixação 1 Adicione no controlador Editora do projeto K19 uma ação para visualizar o formulário de ca- dastro de editoras. 1 ... 2 public ActionResult Cadastra () 3 { 4 return View () ; 5 } 6 ... Código C# 6.17: EditoraController.cs 102 www.k19.com.br
  • 113.
    103 C AMADA DE C ONTROLE 2 Crie arquivo chamado Cadastra.cshtml na pasta ViewsEditora. Nesse arquivo implemente um formulário para cadastrar de editoras. 1 @{ 2 ViewBag . Title = " Cadastra " ; 3 } 4 < h2 > Cadastro de Editoras </ h2 > 5 6 < form action = " / Editora / Salva " > 7 Nome : < input name = " Nome " / > 8 Email : < input name = " Email " / > 9 < input type = " submit " value = " Enviar " / > 10 </ form > Código CSHTML 6.2: Cadastra.cshtml 3 Defina uma ação para salvar editoras no controlador Editora. Essa ação receberá os dados enviados pelo usuário e adicionará uma editora ao banco de dados. Para receber os dados, utilize a propriedade Request. 1 ... 2 public ActionResult Salva () 3 { 4 Editora editora = new Editora { Nome = Request [ " Nome " ] , Email = Request [ " Email " ] }; 5 db . Editoras . Add ( editora ) ; 6 db . SaveChanges () ; 7 return RedirectToAction ( " Index " ) ; 8 } 9 ... Código C# 6.18: EditoraController.cs Para testar, cadastre uma editora acessando a url http://localhost:<PORTA_APP>/Editora/ Cadastra 4 Altere ação criada anteriormente para receber os dados do usuário através do parâmetro do método. 1 ... 2 public ActionResult Salva ( string nome , string email ) 3 { 4 Editora editora = new Editora { Nome = nome , Email = email }; 5 db . Editoras . Add ( editora ) ; 6 db . SaveChanges () ; 7 return RedirectToAction ( " Index " ) ; 8 } 9 ... Código C# 6.19: EditoraController.cs Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra 5 Altere novamente a ação criada anteriormente. Agora, ela deve receber uma editora completa como parâmetro. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 db . SaveChanges () ; 6 return RedirectToAction ( " Index " ) ; 7 } www.k19.com.br 103
  • 114.
    C AMADA DEC ONTROLE 104 8 ... Código C# 6.20: EditoraController.cs Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra TempData Ao efetuar um redirecionamento, uma nova requisição é realizada pelo navegador. Nesta nova requisição, não temos mais acesso aos dados e objetos da requisição anterior ao redirecionamento. Caso haja a necessidade de preservar dados ao longo do redirecionamento, podemos utilizar a pro- priedade TempData. Por exemplo, ao cadastrar uma editora, podemos efetuar um redirecionamento para a tela de listagem de editoras. Na propriedade TempData, podemos acrescentar uma mensagem indicando o eventual sucesso da operação. Veja o exemplo abaixo. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( Editora editora ) 4 { 5 db . Editoras . Add ( editora ) ; 6 db . SaveChanges () ; 7 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 ... Código C# 6.21: EditoraController.cs Na camada de apresentação, podemos recuperar os dados armazenados na propriedade TempData. Veja o código a seguir: 1 @if ( TempData [ " Mensagem " ] != null ) 2 { 3 <p > @TempData [ " Mensagem " ] </ p > 4 } Código CSHTML 6.3: Recuperando dados armazenados na propriedade TempData Exercícios de Fixação 6 Ao adicionar uma editora, o usuário é redirecionado para a ação de listagem de editoras. Mos- tre uma mensagem na página de listagem para indicar que a operação foi realizada com sucesso. Inicialmente, tente armazenar essa mensagem na ViewBag. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 db . SaveChanges () ; 6 ViewBag . Mensagem = " Editora cadastrada com sucesso ! " ; 7 return RedirectToAction ( " Index " ) ; 8 } 104 www.k19.com.br
  • 115.
    105 C AMADA DE C ONTROLE 9 ... Código C# 6.22: EditoraController.cs 7 Altere o arquivo Index.cshtml da pasta ViewsEditora para mostrar a mensagem armazena na ViewBag. 1 @model IEnumerable < K19 . Models . Editora > 2 3 @{ 4 ViewBag . Title = " Index " ; 5 } 6 7 < h2 > Index </ h2 > 8 9 @if ( ViewBag . Mensagem != null ) 10 { 11 < h2 > Mensagem : @ViewBag . Mensagem </ h2 > 12 } 13 14 <p > 15 @Html . ActionLink ( " Create New " , " Create " ) 16 </ p > 17 < table > 18 < tr > 19 < th > 20 @Html . DisplayNameFor ( model = > model . Nome ) 21 </ th > 22 < th > 23 @Html . DisplayNameFor ( model = > model . Email ) 24 </ th > 25 < th > </ th > 26 </ tr > 27 28 @foreach ( var item in Model ) { 29 < tr > 30 < td > 31 @Html . DisplayFor ( modelItem = > item . Nome ) 32 </ td > 33 < td > 34 @Html . DisplayFor ( modelItem = > item . Email ) 35 </ td > 36 < td > 37 @Html . ActionLink ( " Edit " , " Edit " , new { id = item . Id }) | 38 @Html . ActionLink ( " Details " , " Details " , new { id = item . Id }) | 39 @Html . ActionLink ( " Delete " , " Delete " , new { id = item . Id }) 40 </ td > 41 </ tr > 42 } 43 </ table > Código CSHTML 6.4: Index.cshtml Teste a mensagem cadastrando uma editora através da url http://localhost:<PORTA_APP>/Editora/ Cadastra. Verifique que a mensagem de sucesso não aparecerá. 8 Armazene a mensagem de sucesso na propriedade TempData ao invés da ViewBag. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 db . Editoras . Add ( editora ) ; 5 db . SaveChanges () ; 6 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 7 return RedirectToAction ( " Index " ) ; 8 } www.k19.com.br 105
  • 116.
    C AMADA DEC ONTROLE 106 9 ... Código C# 6.23: EditoraController.cs 9 Recupere a mensagem de sucesso da propriedade TempData ou invés da ViewBag. 1 2 @model IEnumerable < K19 . Models . Editora > 3 4 @{ 5 ViewBag . Title = " Index " ; 6 } 7 8 < h2 > Index </ h2 > 9 10 @if ( TempData [ " Mensagem " ] != null ) 11 { 12 < h2 > Mensagem : @TempData [ " Mensagem " ] </ h2 > 13 } 14 15 <p > 16 @Html . ActionLink ( " Create New " , " Create " ) 17 </ p > 18 < table > 19 < tr > 20 < th > 21 @Html . DisplayNameFor ( model = > model . Nome ) 22 </ th > 23 < th > 24 @Html . DisplayNameFor ( model = > model . Email ) 25 </ th > 26 < th > </ th > 27 </ tr > 28 29 @foreach ( var item in Model ) { 30 < tr > 31 < td > 32 @Html . DisplayFor ( modelItem = > item . Nome ) 33 </ td > 34 < td > 35 @Html . DisplayFor ( modelItem = > item . Email ) 36 </ td > 37 < td > 38 @Html . ActionLink ( " Edit " , " Edit " , new { id = item . Id }) | 39 @Html . ActionLink ( " Details " , " Details " , new { id = item . Id }) | 40 @Html . ActionLink ( " Delete " , " Delete " , new { id = item . Id }) 41 </ td > 42 </ tr > 43 } 44 </ table > Código CSHTML 6.5: Index.cshtml Teste a mensagem cadastrando uma editora através da url http://localhost:<PORTA_APP>/Editora/Cadastra. Verifique que a mensagem de sucesso aparecerá. 106 www.k19.com.br
  • 117.
    CAPÍTULO R OTAS 7 Para acessar uma determinada ação da nossa aplicação, os usuários devem realizar uma requi- sição HTTP utilizando a url correspondente à ação. Por exemplo, para acessar a listagem de edito- ras, é necessário digitar na barra de endereço do navegador a url http://localhost:<PORTA_APP> /Editora/Index. Perceba que o o nome do controlador e o nome da ação desejados fazem parte da url. O formato dessas urls é definido por rotas criadas no arquivo RouteConfig.cs e carregadas no arquivo Global.asax. O código abaixo mostra a rota padrão inserida nos projetos ASP.NET MVC 4. 1 routes . MapRoute ( 2 name : " Default " , 3 url : " { controller }/{ action }/{ id } " , 4 defaults : new { controller = " Home " , action = " Index " , id = UrlParameter . Optional } 5 ); Código C# 7.1: RouteConfig.cs O primeiro argumento do método MapRoute é o nome da rota, o segundo é a expressão que define o formato da rota e o terceiro é o conjunto de valores padrão dos parâmetros da rota. A expressão que determina o formato da rota Default utiliza três parâmetros: controller, action e id. Dessa forma, se o usuário realizar uma requisição HTTP utilizando url http://localhost: <PORTA_APP>/Editora/Remove/1, o ASP.NET MVC criará uma instância do controlador Editora e executará o método Remove() passando o valor 1 como argumento. Basicamente, as rotas associam urls e ações. Veja alguns exemplos de como as urls serão proces- sadas de acordo com as regras de rotas. URL Mapeamento da URL / controller = "Home", action = "Index" /Livro controller = "Livro", action = "Index" /Livro/Adiciona controller = "Livro", action = "Adiciona" /Livro/Remove/1 controller = "Livro", action = "Remove", id = 1 Adicionando uma rota Para acrescentar uma rota, podemos utilizar o método MapRoute(). Suponha que a listagem de editoras deva ser acessada através da url http://localhost:<PORTA_APP>/Catalogo. Nesse caso, podemos adicionar uma rota no arquivo RoutesConfig.cs, como mostrado abaixo. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo " , www.k19.com.br 107
  • 118.
    R OTAS 108 5 defaults : new { controller = " Editora " , action = " Index " } 6 ); 7 ... Código C# 7.2: RoutesConfig.cs Importante As rotas são processadas na ordem em que foram inseridas. Portanto, é importante definir as rotas mais específicas antes das rotas mais genéricas. Adicionando Parâmetros nas Rotas Podemos acrescentar parâmetros às rotas. Por exemplo, na rota criada anteriormente para a listagem de editoras, poderíamos adicionar um parâmetro para limitar a quantidade de editoras lis- tadas. 1 routes . MapRoute ( 2 name : " Nova Rota " , 3 url : " Catalogo /{ maximo } " , 4 defaults : new { controller = " Editora " , action = " Index " } 5 ); Código C# 7.3: RoutesConfig.cs Por padrão, os parâmetros adicionados a uma rota são obrigatórios. Dessa forma, no exemplo acima, a listagem de editoras só poderá ser acessada se o valor do parâmetro maximo for definido na url da requisição. Por exemplo, http://localhost:<PORTA_APP>/Catalogo/20. Se uma requisição for realizada para a url http://localhost:<PORTA_APP>/Catalogo, um erro ocorrerá. Para mudar esse comportamento, podemos tornar o parâmetro maximo opcional. Veja o código a seguir. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo /{ maximo } " , 5 defaults : new { controller = " Editora " , action = " Index " , 6 maximo = UrlParameter . Optional } 7 ); 8 ... Código C# 7.4: RoutesConfig.cs Os parâmetros das rotas podem ser recuperados nas ações. Observe o exemplo abaixo. 1 ... 2 public ActionResult Index ( int ? maximo ) 3 { 4 // implementação 5 } 6 ... Código C# 7.5: EditoraController.cs Ao definir parâmetros opcionais, devemos utilizar parâmetros do tipo nullable type nas ações, pois quando o parâmetro não estiver definido na url de uma requisição, o valor null será atribuído 108 www.k19.com.br
  • 119.
    109 R OTAS ao parâmetro da ação. Por exemplo, para os tipos int e double, devemos utilizar int? e double?, respectivamente. Exercícios de Fixação 1 Acrescente uma nova rota no projeto K19 para acessarmos a listagem de editoras através da url http://localhost:<PORTA_APP>/Catalogo. Para isso, altere o arquivo RouteConfig.cs da pasta App_Start. Essa nova rota deve ser adiciona antes da rota Default já definida no arquivo RouteCon- fig.cs. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo " , 5 defaults : new { controller = " Editora " , action = " Index " } 6 ); 7 ... Código C# 7.6: RouteConfig.cs Teste essa nova rota, acessando a url http://localhost:<PORTA_APP>/Catalogo. 2 Acrescente um parâmetro chamado prefixo à rota criada no exercício anterior. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo /{ prefixo } " , 5 defaults : new { controller = " Editoras " , action = " Index " } 6 ); 7 ... Código C# 7.7: RouteConfig.cs Faça um teste acessando a url http://localhost:<PORTA_APP>/Catalogo. Verifique que um erro é gerado. Depois tente acessar a url http://localhost:<PORTA_APP>/Catalogo/teste. Verifi- que que a requisição é processada com sucesso, pois o parâmetro prefixo foi definido. 3 Faça o parâmetro prefixo ser opcional. 1 ... 2 routes . MapRoute ( 3 name : " Nova Rota " , 4 url : " Catalogo /{ prefixo } " , 5 defaults : new { controller = " Editoras " , action = " Index " , 6 prefixo = UrlParameter . Optional } 7 ); 8 ... Código C# 7.8: RouteConfig.cs Agora, tente acessar a url http://localhost:<PORTA_APP>/Catalogo. Nenhum erro é obtido dessa vez. 4 Altere a ação de listagem de editoras para receber o parâmetro prefixo da rota definida anteri- ormente. Defina a lógica para listar as editoras a partir desse parâmetro. www.k19.com.br 109
  • 120.
    R OTAS 110 1 ... 2 public ActionResult Index ( string prefixo ) 3 { 4 if ( String . IsNullOrEmpty ( prefixo ) ) 5 { 6 return View ( db . Editoras . ToList () ) ; 7 } 8 else 9 { 10 var consulta = from e in db . Editoras 11 where 12 e . Nome . StartsWith ( prefixo ) 13 select e ; 14 return View ( consulta . ToList () ) ; 15 } 16 } 17 ... Código C# 7.9: EditorasController.cs Para testar, acesse a url http://localhost:<PORTA_APP>/Catalogo/<PREFIXO> com diferentes prefixos. 110 www.k19.com.br
  • 121.
    CAPÍTULO VALIDAÇÃO 8 Os usuários podem cometer erros ao preencher um formulário. Por exemplo, esquecer de preen- cher um campo que é obrigatório. Os parâmetros enviados pelos usuários devem ser validados pela aplicação com o intuito de não permitir o armazenamento de informações erradas. Controller O primeiro passo para implementar a validação dos parâmetros enviados através de formulários HTML é definir a lógica de validação na camada de controle. 1 if ( editora . Nome == null || editora . Nome . Trim () . Length == 0) 2 { 3 // Erro de Validação 4 } Código C# 8.1: Definindo as regras de validação O segundo passo é definir mensagens informativas para enviar aos usuários. O ASP.NET MVC possui um objeto especializado no armazenamento de mensagens de erros de validação. Esse objeto pode ser acessado através da propriedade ModelState. 1 if ( editora . Nome == null || editora . Nome . Trim () . Length == 0) 2 { 3 ModelState . AddModelError ( " Nome " , " O campo Nome é obrigatório " ) ; 4 } Código C# 8.2: Definindo as mensagens de erro de validação As mensagens são armazenadas no ModelState através do método AddModelError(). Esse mé- todo permite que as mensagens sejam agrupadas logicamente, pois ele possui dois parâmetros: o primeiro é o grupo da mensagem e o segundo e a mensagem propriamente. O código de validação pode ser colocado nos controladores, mais especificamente nas ações. Se algum erro for encontrado, o fluxo de execução pode ser redirecionado para uma página que mostre as mensagens informativas aos usuários. Normalmente, essa página é a mesma do formulário que foi preenchido incorretamente. O ModelState possui uma propriedade que indica se erros foram adicionados ou não. Essa propriedade chama-se IsValid. 1 [ HttpPost ] 2 public ActionResult Salva ( Editora editora ) 3 { 4 if ( editora . Nome == null || editora . Nome . Trim () . Length == 0) 5 { 6 ModelState . AddModelError ( " Nome " , " O campo Nome é obrigatório . " ) ; 7 } 8 if ( ModelState . IsValid ) 9 { www.k19.com.br 111
  • 122.
    VALIDAÇÃO 112 10 db . Editoras . Add ( editora ) ; 11 TempData [ " mensagem " ] = " A editora foi cadastrada com sucesso ! " ; 12 return RedirectToAction ( " Index " ) ; 13 } 14 else 15 { 16 return View ( " Cadastra " ) ; 17 } 18 } Código C# 8.3: EditoraController.cs O ASP.NET MVC também pode adicionar mensagens no ModelState antes do controlador ser chamado. Normalmente, essas mensagens estão relacionadas a erros de conversão. Por exemplo, um campo que espera um número é preenchido com letras. View As mensagens de erros de validação podem ser acrescentadas em uma página através do método ValidationSummary() da propriedade Html. É importante salientar que esse método adiciona todas as mensagens de erro. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Cadastro de Editora " ; 5 } 6 7 < h2 > Cadastro de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary () 11 12 < br / > 13 14 @Html . LabelFor ( model = > model . Nome ) 15 @Html . EditorFor ( model = > model . Nome ) 16 17 < br / > 18 19 @Html . LabelFor ( model = > model . Email ) 20 @Html . EditorFor ( model = > model . Email ) 21 22 < br / > 23 24 < input type = " submit " value = " Enviar " / > 25 } Código CSHTML 8.1: Cadastra.cshtml Podemos utilizar o método ValidationMessageFor() para mostrar somente as mensagens de erro de validação de um determinado grupo. Para não mostrar erros dos grupos com o ValidationSummary(), devemos passar como parâme- tro o valor true. Nesse caso, apenas os erros que não estão associados a um grupo específico serão apresentados pelo ValidationSummary(). 1 @model LivrariaVirtual . Models . Editora 2 3 @{ 112 www.k19.com.br
  • 123.
    113 VALIDAÇÃO 4 ViewBag . Title = " Cadastro de Editora " ; 5 } 6 7 < h2 > Cadastro de Editora </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 12 < br / > 13 14 @Html . LabelFor ( model = > model . Nome ) 15 @Html . EditorFor ( model = > model . Nome ) 16 @Html . ValidationMessageFor ( model = > model . Nome ) 17 18 < br / > 19 20 @Html . LabelFor ( model = > model . Email ) 21 @Html . EditorFor ( model = > model . Email ) 22 @Html . ValidationMessageFor ( model = > model . Email ) 23 24 < br / > 25 26 < input type = " submit " value = " Enviar " / > 27 } Código CSHTML 8.2: Cadastra.cshtml Exercícios de Fixação 1 Defina as regras de validação dos campos das editoras. A editora deve ter obrigatoriamente nome e email. Para exibir as mensagens sobre os erros de validação, utilize os métodos ValidationMessage() e ValidationMessageFor(). No projeto K19, altera a lógica da ação Salva do controlador Editora. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 if ( String . IsNullOrEmpty ( editora . Nome ) ) 5 { 6 ModelState . AddModelError ( " Nome " , " O nome da editora é obrigatório " ) ; 7 } 8 if ( String . IsNullOrEmpty ( editora . Email ) ) 9 { 10 ModelState . AddModelError ( " Email " , " O email da editora é obrigatório " ) ; 11 } 12 13 if ( ModelState . IsValid ) 14 { 15 db . Editoras . Add ( editora ) ; 16 db . SaveChanges () ; 17 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 18 return RedirectToAction ( " Index " ) ; 19 } 20 else 21 { 22 return View ( " Cadastra " , editora ) ; 23 } 24 } 25 ... Código C# 8.4: EditoraController.cs www.k19.com.br 113
  • 124.
    VALIDAÇÃO 114 2 Modifique o formulário de cadastro de editoras. 1 @model K19 . Models . Editora 2 3 @{ 4 ViewBag . Title = " Cadastra " ; 5 } 6 7 < h2 > Cadastro de Editoras </ h2 > 8 9 @using ( Html . BeginForm ( " Salva " , " Editora " ) ) 10 { 11 @Html . LabelFor ( x = > x . Nome ) 12 @Html . EditorFor ( x = > x . Nome ) 13 @Html . ValidationMessageFor ( x = > x . Nome ) 14 15 < br / > 16 17 @Html . LabelFor ( x = > x . Email ) 18 @Html . EditorFor ( x = > x . Email ) 19 @Html . ValidationMessageFor ( x = > x . Email ) 20 21 < br / > 22 23 < input type = " submit " value = " Enviar " / > 24 } Código CSHTML 8.3: Cadastra.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra. Deixe o campo nome e email em branco e verifique as mensagens de erro de validação. Anotações As lógicas de validação também podem ser definidas através de anotações adicionadas nas clas- ses de modelo. Dessa forma, essas lógicas não estariam mais nos controladores, o que conceitual- mente é o ideal, pois nos controladores só deveria existir lógica para controlar o fluxo da execução. Required Uma das validações mais comuns é a de campo obrigatório. Ela pode ser realizada através da anotação Required. 1 public class Editora 2 { 3 [ Required ] 4 public string Nome { get ; set ;} 5 6 ... 7 } Código C# 8.5: Editora.cs Com essa anotação, a lógica de validação pode ser retirada do controlador Editora. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( Editora editora ) 114 www.k19.com.br
  • 125.
    115 VALIDAÇÃO 4 { 5 if ( ModelState . IsValid ) 6 { 7 db . Editoras . Add ( editora ) ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 else 11 { 12 return View ( " Cadastra " , editora ) ; 13 } 14 } 15 ... Código C# 8.6: EditoraController.cs Alterando a mensagem As anotações de validação possuem mensagens padrão que podem ser alteradas através do atri- buto ErrorMessage. 1 ... 2 [ Required ( ErrorMessage = " O campo Nome é obrigatório " ) ] 3 public string Nome { get ; set ; } 4 ... Outros validadores Há outras anotações para validação: • Range • ReqularExpression • StringLength Validação no lado do Cliente As validações podem ser realizadas também nos navegadores para melhorar a interação com os usuários. Antes da terceira versão do ASP .NET MVC, era necessário habilitar a validação no lado do cliente através do método Html.EnableClientValidation(). A partir da terceira versão do ASP.NET MVC, a validação no cliente está habilitada por padrão. Para que a validação no lado cliente funcione corretamente, devemos acrescentar as bibliotecas javascript necessárias. No ASP.NET MVC 4, basta acrescentar a seção de scripts nas páginas. 1 @section Scripts { 2 @Scripts . Render ( " ~/ bundles / jqueryval " ) 3 } www.k19.com.br 115
  • 126.
    VALIDAÇÃO 116 Exercícios de Fixação 3 Altere as validações feitas anteriormente, para utilizar DataAnnotations. Lembre-se de alterar todas as mensagens de erro para a língua portuguesa. Acrescente também a validação no cliente. Primeiramente, altere a camada de modelo. 1 using System . ComponentModel . DataAnnotations ; 2 3 namespace K19 . Models 4 { 5 public class Editora 6 { 7 public int Id { get ; set ; } 8 [ Required ( ErrorMessage = " O campo nome é obrigatório " ) ] 9 public string Nome { get ; set ; } 10 [ Required ( ErrorMessage = " O campo email é obrigatório " ) ] 11 public string Email { get ; set ; } 12 } 13 } Código C# 8.8: Editora.cs 4 Altere a ação Salva retirando a lógica de validação definida anteriormente. 1 ... 2 public ActionResult Salva ( Editora editora ) 3 { 4 if ( ModelState . IsValid ) 5 { 6 db . Editoras . Add ( editora ) ; 7 db . SaveChanges () ; 8 TempData [ " Mensagem " ] = " Editora cadastrada com sucesso ! " ; 9 return RedirectToAction ( " Index " ) ; 10 } 11 else 12 { 13 return View ( " Cadastra " , editora ) ; 14 } 15 } 16 ... Código C# 8.9: EditoraControlle.cs 5 Para adicionar a validação no lado do cliente, basta alterar a página de cadastro de editoras. Observe o código abaixo. 1 @model K19 . Models . Editora 2 3 < h2 > Cadastro de Editoras </ h2 > 4 @using ( Html . BeginForm ( " Salva " , " Editora " ) ) 5 { 6 @Html . LabelFor ( x = > x . Nome ) 7 @Html . EditorFor ( x = > x . Nome ) 8 @Html . ValidationMessageFor ( x = > x . Nome ) 9 10 < br / > 11 12 @Html . LabelFor ( x = > x . Email ) 13 @Html . EditorFor ( x = > x . Email ) 14 @Html . ValidationMessageFor ( x = > x . Email ) 15 116 www.k19.com.br
  • 127.
    117 VALIDAÇÃO 16 < br / > 17 18 < input type = " submit " value = " Enviar " / > 19 20 } 21 @section Scripts { 22 @Scripts . Render ( " ~/ bundles / jqueryval " ) 23 } Código CSHTML 8.5: Cadastra.cshtml Para testar, acesse a url http://localhost:<PORTA_APP>/Editora/Cadastra. Deixe o campo nome e email em branco e verifique as mensagens de erro de validação. www.k19.com.br 117
  • 128.
    VALIDAÇÃO 118 118 www.k19.com.br
  • 129.
    CAPÍTULO SESSÃO 9 Considere a aplicação de uma loja virtual. Nessa aplicação, os clientes selecionam os produtos desejados e os adiciona no seu carrinho de compra. Cada cliente deve ter o seu próprio carrinho para que os seus produtos não se misturem com os produtos selecionados por outros clientes. A aplicação deve armazenar o carrinho de um cliente até que a compra seja finalizada ou até ela ter certeza que o cliente não precisa mais do carrinho. Para resolver esse problema, podemos utilizar o conceito de Sessão. Para cada navegador co- nectado, o servidor manterá uma sessão aberta. Dessa forma, podemos separar os dados de cada usuário conectado. Identificando os navegadores Para aplicar a ideia de Sessão, é necessário ter a capacidade de identificar o navegador que está requisitando a aplicação a cada requisição. Uma primeira abordagem seria utilizar o endereço IP da máquinas para identificar os navegadores. Porém, nessa abordagem, dois navegadores executando na mesma máquina não poderiam ser identificados individualmente. Outra abordagem é deixar a cargo do servidor a criação de um identificador único para cada navegador conectado. Quando um navegador faz a primeira requisição para a aplicação, o servi- dor deve gerar um identificador único para esse navegador e enviá-lo na resposta HTTP. A partir da segunda requisição, os navegadores devem enviar para a aplicação o identificador recebido na pri- meira requisição. Desta maneira, a aplicação saberá qual é o navegador que está realizando uma requisição. Os navegadores podem enviar os seus respectivos identificadores de diferentes formas. As mais utilizadas são: Reescrita de URL Nesta abordagem, os identificadores são embutidos nos links e botões das páginas da aplicação. Quando os links ou botões são clicados pelo usuário, o identificador é enviado para a aplicação. Uma desvantagem é que todas as páginas devem ser geradas dinamicamente para adicionar o identificador em todos os links e botões. Cookies Cookies são arquivos contendo informações. Eles são gerados nos servidores e enviados para os navegadores. Os navegadores armazenam os cookies localmente na máquina do usuá- rio. Além disso, os navegadores enviam os cookies de volta para o servidor em todas as requi- sições. Os servidores podem armazenar os identificadores gerados em cookies. Dessa forma, a cada requisição, o servidor receberá um cookie contendo o identificador. Sessões no ASP.NET MVC www.k19.com.br 119
  • 130.
    S ESSÃO 120 No ASP.NET MVC, o objeto que representa uma sessão é um dicionário. Para armazenar infor- mações, você deve adicionar uma chave e um valor na propriedade Session. Considere um objeto da classe Usuario que agrupa as informações sobre um determinado usuário. O código a seguir é um exemplo de como podemos guardar esse objeto na sessão após a realização da autenticação do usuário. 1 public class LoginController : Controller 2 { 3 ... 4 public ActionResult Login ( Cliente cliente ) 5 { 6 ... 7 Session [ " Cliente " ] = cliente ; 8 ... 9 } 10 } Código C# 9.1: LonginController.cs Você pode adicionar qualquer tipo de valor na sessão. De forma análoga, para resgatar as infor- mações armazenadas, basta acessar a chave correspondente da propriedade Session. Veja o exem- plo a seguir: 1 Cliente cliente = ( Cliente ) Session [ " Cliente " ]; 2 string saudacao = " Bem vindo " + cliente . Nome ; Código C# 9.2: Recuperando informações da sessão Quando um usuário deslogar, podemos eliminar a informação armazenada em sua sessão. Para isso, podemos simplesmente remover todas as chaves do dicionário como no exemplo a seguir. 1 Session . RemoveAll () ; Código C# 9.3: Eliminando todas as informações da sessão Contudo, o método RemoveAll() não elimina a sessão. Ele apenas remove os dados contidos na sessão. Para eliminá-la, você deve utilizar o método Abandon(). Observe o exemplo a seguir. 1 public class LoginController : Controller 2 { 3 ... 4 public ActionResult Logout () 5 { 6 ... 7 Session . Abandon () ; 8 ... 9 } 10 } Código C# 9.4: LoginController.cs Session Mode O ASP.NET MVC disponibiliza quatro modos de sessão (Session Modes): • InProc 120 www.k19.com.br
  • 131.
    121 S ESSÃO • StateServer • SQLServer • Custom A diferença entre eles é o modo de armazenamento das sessões. No modo InProc, todas as informações da sessão são armazenadas na memória do servidor. Esse é o modo mais simples e mais utilizado, e vem configurado como o padrão. Uma desvantagem desse modo é a possível sobrecarga de memória se forem armazenadas muitas informações na sessão. Por isso não é indicado para sistemas muito grandes, com muitos usuários navegando simultaneamente. Outro problema é que o servidor da aplicação não pode ser desligado, pois a informação armazenada na memória será perdida. No modo StateServer, as informações são serializadas e enviadas para um servidor indepen- dente do servidor da aplicação. Isto possibilita que o servidor de aplicação possa ser reiniciado sem que as sessões sejam perdidas. Uma desvantagem é o tempo gasto para realizar a serialização e des- serialização. No modo SQLServer, as informações também são serializadas, mas são armazenadas em um banco de dados. Além de permitir que o servidor da aplicação seja reiniciado, este modo possibi- lita um maior controle de segurança dos dados da sessão. Uma desvantagem importante é que o processo é naturalmente lento. No modo Custom, todo o processo de identificação de usuários e armazenamento de sessões pode ser personalizado. Para selecionar algum dos modos disponíveis, basta adicionar a tag <sessionState> dentro da tag <system.web>, no arquivo Web.Config da raiz de um projeto ASP.NET MVC 4. 1 < sessionState mode = " InProc " / > Código XML 9.1: Web.Config Podemos personalizar nossa sessão, modificando alguns atributos da tag <sessionState>. Por exemplo, podemos determinar o tempo de timeout das sessões. Veja o exemplo abaixo. 1 < sessionState mode = " InProc " timeout = " 30 " / > Código XML 9.2: Web.Config Outra possibilidade é desabilitar o uso de cookie com o atributo cookieless. Neste caso será utilizada a reescrita de URL. 1 < sessionState mode = " InProc " cookieless = " true " / > Código XML 9.3: Web.Config Exercícios de Fixação www.k19.com.br 121
  • 132.
    S ESSÃO 122 1 Para entender o conceito de sessão, vamos mostrar o nome do usuário logado em todas as páginas. Primeiramente, adicione uma classe chamada Usuario na pasta Models do projeto K19. 1 namespace K19 . Models 2 { 3 public class Usuario 4 { 5 public string Username { get ; set ; } 6 public string Password { get ; set ; } 7 } 8 } Código C# 9.5: Usuario.cs 2 Agora, vamos definir um controlador para implementar o processo de autenticação. Adicione um controlador chamado Autenticador na pasta Controllers do projeto K19. Implemente as ações para o usuário logar-se e deslogar-se do sistema. 1 using System . Web . Mvc ; 2 using K19 . Models ; 3 4 namespace K19 . Controllers 5 { 6 public class AutenticadorController : Controller 7 { 8 9 public ActionResult Formulario () 10 { 11 return View () ; 12 } 13 14 public ActionResult Entrar ( Usuario usuario ) 15 { 16 if ( usuario . Username != null && usuario . Password != null && 17 usuario . Username . Equals ( " K19 " ) && usuario . Password . Equals ( " K19 " ) ) { 18 Session [ " Usuario " ] = usuario ; 19 return RedirectToAction ( " Index " , " Editora " ) ; 20 } 21 else 22 { 23 ViewBag . Mensagem = " usuário ou senha incorretos " ; 24 return View ( " Formulario " ) ; 25 } 26 } 27 28 public ActionResult Sair () 29 { 30 Session . Abandon () ; 31 return RedirectToAction ( " Formulario " ) ; 32 } 33 } 34 } Código C# 9.6: AutenticadorController.cs 3 Crie um arquivo chamado Formulario.cshtml em uma pasta chamada ViewsAutenticador com o seguinte conteúdo. 1 @model K19 . Models . Usuario 2 3 @{ 4 ViewBag . Title = " Formulario " ; 5 } 122 www.k19.com.br
  • 133.
    123 S ESSÃO 6 7 @if ( ViewBag . Mensagem != null ) 8 { 9 < h2 > @ViewBag . Mensagem </ h2 > 10 } 11 12 < h2 > Formulario de Autenticação </ h2 > 13 @using ( Html . BeginForm ( " Entrar " , " Autenticador " ) ) 14 { 15 @Html . LabelFor ( x = > x . Username ) 16 @Html . EditorFor ( x = > x . Username ) 17 18 < br / > 19 20 @Html . LabelFor ( x = > x . Password ) 21 @Html . EditorFor ( x = > x . Password ) 22 23 < br / > 24 25 < input type = " submit " value = " Enviar " / > 26 } Código CSHTML 9.1: Formulario.cshtml Faça um teste acessando a url http://localhost:<PORTA_APP>/Autenticador/Formulario. Tente acessar o sistema com usuários ou senhas incorretos. Depois, utilize o usuário K19 e a senha K19. 4 Altere o arquivo K19Layout.cshtml acrescentando links para o usuário logar-se e deslogar-se (caso estiver logado) do sistema. 1 @using K19 . Models 2 3 <! DOCTYPE html > 4 < html > 5 < head > 6 < meta charset = " utf -8 " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 < title > @ViewBag . Title </ title > 9 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 10 @Scripts . Render ( " ~/ bundles / modernizr " ) 11 </ head > 12 < body > 13 < div id = " header " > 14 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 15 16 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 17 18 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 19 20 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 21 22 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 23 24 @if ( Session [ " Usuario " ] != null ) 25 { 26 < span > Olá @ (( Session [ " Usuario " ] as Usuario ) . Username ) ! </ span > 27 @Html . ActionLink ( " Logout " , " Sair " , " Autenticador " ) 28 } 29 else 30 { 31 @Html . ActionLink ( " Login " , " Formulario " , " Autenticador " ) 32 } 33 </ div > 34 @if ( IsSectionDefined ( " Sidebar " ) ) 35 { 36 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 37 } www.k19.com.br 123
  • 134.
    S ESSÃO 124 38 else 39 { 40 < div id = " sidebar " > Sidebar padrão </ div > 41 } 42 < div id = " content " > @RenderBody () </ div > 43 < div id = " footer " > K19 Treinamentos </ div > 44 45 @Scripts . Render ( " ~/ bundles / jquery " ) 46 @RenderSection ( " scripts " , required : false ) 47 </ body > 48 </ html > Código CSHTML 9.2: K19Layout.cshtml Para testar, acesse http://localhost:<PORTA_APP>/Autenticador/Formulario. Faça testes logando e deslogando do sistema. 124 www.k19.com.br
  • 135.
    CAPÍTULO AUTENTICAÇÃO 10 Muitas vezes, queremos restringir o acesso à determinadas áreas de uma aplicação, seja por ques- tões de segurança ou por organização. Em nossa aplicação poderíamos, por exemplo, definir que para poder adicionar, alterar e remover editoras, o usuário deve estar logado no sistema. Caso con- trário, o usuário apenas poderá listar as editoras. 1 ... 2 public ActionResult Cadastra () 3 { 4 if ( Session [ " Cliente " ] != null ) 5 { 6 return base . View () ; 7 } 8 else 9 { 10 return base . RedirectToAction ( " Index " , " Login " ) ; 11 } 12 } 13 ... Código C# 10.1: EditoraController.cs No exemplo acima, caso um usuário tente adicionar uma editora através do formulário de ca- dastro, a ação Cadastra verificará se o usuário já está logado. Essa verificação é realizada através do uso da sessão, como visto no capítulo 9. Se o usuário não estiver logado, ele será redirecionado para a página de Login. Apesar de funcionar, este código apresenta uma inconveniência. Temos que adicionar essa lógica em todas as ações que queremos que tenha o mesmo comportamento, ou seja, que apenas permitam o acesso de usuários logados. Em outros casos, podemos desejar que algumas ações executem alguma tarefa em comum. Por exemplo, poderíamos adicionar uma mensagem em um arquivo de logging sempre que uma ação fosse executada. Desse modo, poderíamos guardar um histórico sobre o que a aplicação mais execu- tou, qual foi a página mais visitada, etc. Mas novamente, teríamos que adicionar a mesma tarefa em todas as ações da nossa aplicação. Nesses casos, em que várias ações possuem um comportamento em comum, podemos utilizar o conceito de filtros. Um filtro é semelhante a um método que é executado antes ou depois que uma ação. Filtro de Autenticação O ASP.NET MVC já possui alguns filtros prontos para serem utilizados, como o filtro de auten- ticação. Podemos utilizar ele para o nosso primeiro exemplo, onde exigimos que o usuário esteja logado (autenticado) para acessar determinadas áreas da aplicação. Para isso, precisamos adicionar o seguinte código no nosso método de login: www.k19.com.br 125
  • 136.
    AUTENTICAÇÃO 126 1 [ dots **] 2 FormsAuthentication . SetAuthCookie ( usuario . Username , false ) ; 3 [ dots **] Código C# 10.2: AutenticadorController.cs Isto adiciona um novo cookie utilizado para a autenticação do usuário. Este novo cookie é in- dependente do cookie utilizado para armazenar informações da sessão. O primeiro parâmetro é referente ao nome do usuário (ou algo que o identifique). O segundo parâmetro é um booleano re- lativo ao tipo do cookie, se é permanente ou não. Caso seja true, ele sempre irá considerar que o usuário está autenticado após a primeira autenticação. Para eliminar o cookie de autenticação, devemos adicionar o seguinte código na acão de logout: 1 ... 2 FormsAuthentication . SignOut () ; 3 ... Código C# 10.3: AutenticadorController.cs As ações que devem utilizar o filtro de autenticação devem ser anotadas com Authorize. 1 ... 2 [ Authorize ] 3 public ActionResult Cadastra () 4 { 5 return base . View () ; 6 } 7 ... Código C# 10.4: EditoraController.cs Se queremos aplicar o mesmo filtro a todas as ações de um controlador, podemos adicionar a notação Authorize na classe. 1 [ Authorize ] 2 public class EditoraController : Controller 3 { 4 ... 5 } Código C# 10.5: EditoraController.cs Desse modo, todas as ações presentes no controlador da Editora exigem que o usuário esteja autenticado. Quando o filtro de autenticação “barra” um usuário de acessar uma página, podemos redirecioná- lo para a página de login. Devemos incluir o seguinte código dentro da tag <system.web> do arquivo Web.Config. 1 < authentication mode = " Forms " > 2 < forms loginUrl = " ~/ Autenticador / Formulario " timeout = " 2880 " / > 3 </ authentication > Código XML 10.1: Web.Config 126 www.k19.com.br
  • 137.
    127 AUTENTICAÇÃO Para verificar se o usuário associado à sessão atual está autenticada, podemos utilizar a proprie- dade IsAuthenticated, como a seguir: 1 if ( User . Identity . IsAuthenticated ) 2 { 3 ... 4 } Código C# 10.6: Verificando se o usuário está autenticado Podemos pegar a informação de quem está autenticado através do seguinte comando: 1 string nome = User . Identity . Name ; Código C# 10.7: Verificando quem é o usuário autenticado Isto irá pegar o nome que passamos como parâmetro para o método SetAuthCookie na autenti- cação. Exercícios de Fixação 1 Altere a ação Cadastra do controlador Editora para que somente usuários logados possam acessar o formulário de cadastro. 1 ... 2 public ActionResult Cadastra () 3 { 4 if ( Session [ " Usuario " ] != null ) 5 { 6 return View () ; 7 } 8 else 9 { 10 return RedirectToAction ( " Formulario " , " Autenticador " ) ; 11 } 12 } 13 ... Código C# 10.8: EditorasController.cs Teste acessando o seguinte endereço: http://localhost:<PORTA_APP>/Editora/Cadastra. Ve- rifique que enquanto o login não for efetuado, não será possível acessar o formulário de cadastro. 2 Ao invés de usar sessão, vamos utilizar o filtro de autenticação pronto do ASP.NET MVC. Primeiro, devemos alterar as ações Entrar e Sair do controlador Autenticador. 1 using System . Web . Mvc ; 2 using System . Web . Security ; 3 using K19 . Models ; 4 5 namespace K19 . Controllers 6 { 7 public class AutenticadorController : Controller 8 { 9 10 public ActionResult Formulario () 11 { 12 return View () ; www.k19.com.br 127
  • 138.
    AUTENTICAÇÃO 128 13 } 14 15 public ActionResult Entrar ( Usuario usuario ) 16 { 17 if ( usuario . Username != null && usuario . Password != null && 18 usuario . Username . Equals ( " K19 " ) && usuario . Password . Equals ( " K19 " ) ) { 19 20 FormsAuthentication . SetAuthCookie ( usuario . Username , false ) ; 21 return RedirectToAction ( " Index " , " Editora " ) ; 22 } 23 else 24 { 25 ViewBag . Mensagem = " usuário ou senha incorretos " ; 26 return View ( " Formulario " ) ; 27 } 28 } 29 30 public ActionResult Sair () 31 { 32 FormsAuthentication . SignOut () ; 33 return RedirectToAction ( " Formulario " ) ; 34 } 35 } 36 } Código C# 10.9: AutenticadorController.cs 3 Altere o arquivo de layout para mostrar o usuário logado. 1 @using K19 . Models 2 3 <! DOCTYPE html > 4 < html > 5 < head > 6 < meta charset = " utf -8 " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 < title > @ViewBag . Title </ title > 9 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 10 @Scripts . Render ( " ~/ bundles / modernizr " ) 11 </ head > 12 < body > 13 < div id = " header " > 14 @Html . ActionLink ( " Editoras " , " Index " , " Editora " ) 15 16 @Html . ActionLink ( " Testa Formulario Html " , " TestaFormularioHtml " ," Teste " ) 17 18 @Html . ActionLink ( " Testa Helpers " , " TestaHelpers " ," Teste " ) 19 20 @Html . ActionLink ( " Testa Helpers Tipados " , " TestaHelpersTipados " ," Teste " ) 21 22 @Html . ActionLink ( " Testa EditorFor " , " TestaEditorFor " ," Teste " ) 23 24 @if ( User . Identity . IsAuthenticated ) 25 { 26 < span > Olá @User . Identity . Name </ span > 27 @Html . ActionLink ( " Logout " , " Sair " , " Autenticador " ) 28 } 29 else 30 { 31 @Html . ActionLink ( " Login " , " Formulario " , " Autenticador " ) 32 } 33 </ div > 34 @if ( IsSectionDefined ( " Sidebar " ) ) 35 { 36 < div id = " sidebar " > @RenderSection ( " Sidebar " , required : false ) </ div > 37 } 38 else 39 { 40 < div id = " sidebar " > Sidebar padrão </ div > 128 www.k19.com.br
  • 139.
    129 AUTENTICAÇÃO 41 } 42 < div id = " content " > @RenderBody () </ div > 43 < div id = " footer " > K19 Treinamentos </ div > 44 45 @Scripts . Render ( " ~/ bundles / jquery " ) 46 @RenderSection ( " scripts " , required : false ) 47 </ body > 48 </ html > Código CSHTML 10.1: K19Layout.cshtml 4 Altere o controlador Editora para que somente usuários logados possam acessar o formulário de cadastro. 1 ... 2 [ Authorize ] 3 public ActionResult Cadastra () 4 { 5 return View () ; 6 } 7 ... Código C# 10.10: EditoraController.cs 5 Defina a url para a qual os usuários não logados serão redirecionados ao tentarem acessar o formulário de cadastro de editoras. Altere o arquivo Web.config da raiz do projeto K19. 1 ... 2 < authentication mode = " Forms " > 3 < forms loginUrl = " ~/ Autenticador / Formulario " timeout = " 2880 " / > 4 </ authentication > 5 ... Código XML 10.2: Web.config Acesse o endereço http://localhost:<PORTA_APP>/Editora/Cadastra. Verifique o funciona- mento do filtro de autenticação que redirecionará os usuários que não estão logados para a url defi- nida no arquivo Web.config. www.k19.com.br 129
  • 140.
    AUTENTICAÇÃO 130 130 www.k19.com.br
  • 141.
    CAPÍTULO TRATAMENTO DE E RROS 11 Inevitavelmente, as aplicações estão sujeitas a erros de várias naturezas. Por exemplo, erros po- dem ser gerados pelo preenchimento incorreto dos campos de um formulário. Esse tipo de erro é causado por falhas dos usuários. Nesse caso, é importante mostrar mensagens informativas com o intuito de fazer o próprio usuário corrigir os valores preenchidos incorretamente. Veja o Capítulo 8. Por outro lado, há erros que não são causados por falhas dos usuários. Por exemplo, um erro de conexão com o banco de dados. Nesses casos, é improvável que os usuários possam fazer algo que resolva o problema. E mesmo que pudessem, provavelmente, não seria conveniente esperar que eles o fizessem. Quando um erro desse tipo ocorre, o ASP.NET MVC cria uma página web com informações so- bre o erro e a envia aos usuários. Para usuários locais, o ASP.NET MVC envia uma página web com informações detalhadas do erro ocorrido. Para usuários remotos, a página web enviada não contém informações detalhadas. Em geral, não é conveniente que os usuários recebam detalhes técnicos sobre os erros gerados por falhas da aplicação. A primeira justificativa é que esses detalhes podem confundir os usuários. Figura 11.1: Exemplo de uma página web com informações sobre um erro www.k19.com.br 131
  • 142.
    T RATAMENTO DEE RROS 132 A segunda justificativa é que esses detalhes podem expor alguma falha de segurança da aplicação, deixando-a mais vulnerável a ataques. Try-Catch Os erros de aplicação podem ser identificados através do comando try-catch, que pode ser colocados nos métodos que definem as ações dos controladores. Ao identificar a ocorrência de um erro, os controladores podem devolver uma página web com alguma mensagem para o usuário. 1 ... 2 [ HttpPost ] 3 public ActionResult Salva ( Editora editora ) 4 { 5 try 6 { 7 db . Editoras . Add ( editora ) ; 8 } 9 catch 10 { 11 return View ( " Error " ) ; 12 } 13 14 return RedirectToAction ( " Index " ) ; 15 } 16 ... Código C# 11.1: EditoraController.cs Podemos criar uma página Error.cshtml na pasta ViewsShared. Dessa forma, todo controlador poderá devolver essa página. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Erro </ title > 9 </ head > 10 < body > 11 < h2 > 12 Servidor com problemas 13 </ h2 > 14 <p > 15 Houve um problema em nosso servidor . < br / > 16 Por favor tente novamente dentro de alguns instantes . 17 </ p > 18 </ body > 19 </ html > Código CSHTML 11.1: Error.cshtml As páginas de erro que serão mostradas pelos controladores teriam uma mensagem simples in- formando que houve um erro na aplicação e que não é possível atender a requisição do usuário naquele momento. Inclusive, seria conveniente padronizar a página de erro. Em outras palavras, todos os controladores teriam que mostrar a mesma página. 132 www.k19.com.br
  • 143.
    133 T RATAMENTO DE E RROS Custom Errors Utilizar o comando try-catch nos controladores para lidar com os erros de aplicação não é uma boa alternativa, pois o código do controlador fica mais complexo. Além disso, haveria replicação de código nos controladores, pois provavelmente a página de erro seria padronizada. Para lidar com os erros de aplicação de uma maneira mais prática e fácil de manter, podemos configurar o ASP .NET MVC para utilizar páginas de erro padrão. O primeiro passo é alterar o arquivo de configuração Web.config, acrescentando a tag <customErrors> dentro da tag <system.web>. 1 ... 2 < customErrors mode = " On " > 3 4 </ customErrors > 5 ... Código XML 11.1: Web.Config O atributo mode da tag <customErrors> pode assumir três valores: On: A página de erro padrão será enviada para usuários locais e remotos. Off: A página de erro detalhada será enviada para usuários locais e remotos. RemoteOnly: A página de erro detalhada será enviada para os usuários locais e a padrão para os remotos. Por convenção, o ASP.NET MVC mantém uma página de erro padrão dentro da pasta ViewsShared com o nome Error.cshtml. Vamos alterar este arquivo. O conteúdo da página de erro é basicamente HTML. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Erro </ title > 9 </ head > 10 < body > 11 < h2 > 12 Servidor com problemas 13 </ h2 > 14 <p > 15 Houve um problema no nosso servidor . < br / > 16 Por favor tente novamente dentro de alguns instantes . 17 </ p > 18 </ body > 19 </ html > Código CSHTML 11.2: Error.cshtml Http Errors www.k19.com.br 133
  • 144.
    T RATAMENTO DEE RROS 134 Um dos erros mais conhecidos do HTTP é o 404, que ocorre quando o navegador faz uma re- quisição para uma url que não existe. Basicamente, esse erro é gerado por falhas dos usuários ao tentarem digitar diretamente uma url na barra de endereço dos navegadores ou por links ou botões “quebrados” nas páginas da aplicação. Quando o erro 404 ocorre, o ASP .NET MVC utiliza a página padrão para erros de aplicação con- figurada no Web.config através da tag <customErrors>. Porém, esse erro não deve ser considerado um erro de aplicação, pois ele pode ser gerado por falhas do usuário. Ele também não deve ser con- siderado um erro de usuário, pois ele pode ser gerado por falhas da aplicação. Consequentemente, é comum tratar o erro 404 de maneira particular, criando uma página de erro específica para ele. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Arquivo não encontrado ! </ title > 9 </ head > 10 < body > 11 < h2 > 12 Esse arquivo não foi encontrado . Verifique se a url está correta . 13 </ h2 > 14 </ body > 15 </ html > Código CSHTML 11.3: NotFound.cshtml No arquivo de configuração, podemos especificar uma página web particular para o erro 404 ou para os outros erros do HTTP. 1 ... 2 < customErrors mode = " On " > 3 < error statusCode = " 404 " redirect = " ~/ ErrorPage / NotFound " / > 4 </ customErrors > 5 ... Código XML 11.2: Web.Config De acordo com o código anterior, devemos definir um controlador chamado ErrorPage com uma ação chamada NotFound. Essa ação será acionada toda vez que o erro 404 do HTTP ocorrer. 1 namespace K19 . Controllers 2 { 3 public class ErrorPageController : Controller 4 { 5 public ActionResult NotFound () 6 { 7 return View () ; 8 } 9 } 10 } Código C# 11.2: ErroPageController.cs Exercícios de Fixação 134 www.k19.com.br
  • 145.
    135 T RATAMENTO DE E RROS 1 Crie uma nova ação no controlador Teste do projeto K19, conforme o código abaixo. 1 ... 2 public ActionResult TestaErro () 3 { 4 string [] nomes = new string [] { " Jonas Hirata " , " Rafael Cosentino " }; 5 string nome = nomes [2]; 6 return View () ; 7 } 8 ... Código C# 11.3: TesteController.cs Acesse o seguinte endereço http://localhost:<PORTA_APP>/Teste/TestaErro e verifique a tela de erro. Observação: No Visual Web Developer, quando executamos a aplicação, ele a executa em modo debug. Dessa forma, toda vez que um erro for gerado no processamento de uma requisição, a exe- cução da aplicação é suspensa no ponto em que o erro ocorreu e detalhes sobre o problema são apresentados. Para continuar a execução da aplicação após a ocorrência de um erro, aperte a tecla F5. 2 Trate o erro do exercício anterior com o bloco try-catch e redirecione o usuário para uma tela com a seguinte mensagem “Sistema Temporariamente Indisponível”. Altere a ação TestaErro do controlador Teste. 1 ... 2 public ActionResult TestaErro () 3 { 4 try 5 { 6 string [] nomes = new string [] { " Jonas Hirata " , " Rafael Cosentino " }; 7 string nome = nomes [2]; 8 return View () ; 9 } 10 catch 11 { 12 return View ( " Error " ) ; 13 } 14 } 15 ... Código C# 11.4: TesteController.cs 3 Altere o código do arquivo Error.cshtml da pasta ViewsShared conforme o código abaixo: 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < meta name = " viewport " content = " width = device - width " / > 9 < title > Error </ title > 10 </ head > 11 < body > 12 < h2 > 13 Sistema Temporariamente Indisponível ... 14 </ h2 > www.k19.com.br 135
  • 146.
    T RATAMENTO DEE RROS 136 15 </ body > 16 </ html > Código CSHTML 11.4: Error.cshtml Acesse novamente o endereço http://localhost:<PORTA_APP>/Teste/TestaErro e verifique a nova página de erro. 4 Remova o bloco try-catch da ação TestaErro do controlador Teste que você adicionou no exercício anterior. 1 ... 2 public ActionResult TestaErro () 3 { 4 string [] nomes = new string [] { " Jonas Hirata " , " Rafael Cosentino " }; 5 string nome = nomes [2]; 6 return View () ; 7 } 8 ... Código C# 11.5: TesteController.cs 5 Altere o arquivo Web.config para configurar o ASP.NET MVC para utilizar páginas de erro pa- drão. 1 ... 2 < system . web > 3 < customErrors mode = " On " > 4 </ customErrors > 5 ... 6 < system . web > 7 ... Código XML 11.3: Web.config Acesse o endereço http://localhost:<PORTA_APP>/Teste/TestaErro e verifique a página de erro. 6 Vamos definir uma página para ser exibida quando o erro 404 ocorrer. Primeiramente, crie um controlador chamado ErrorPage com uma ação chamada NotFound. 1 namespace K19 . Controllers 2 { 3 public class ErrorPageController : Controller 4 { 5 public ActionResult NotFound () 6 { 7 return View () ; 8 } 9 } 10 } Código C# 11.6: ErroPageController.cs 7 Na pasta ViewsErrorPage do projeto K19, adicione um arquivo chamado NotFound.cshtml com o seguinte conteúdo. 1 @{ 2 Layout = null ; 3 } 136 www.k19.com.br
  • 147.
    137 T RATAMENTO DE E RROS 4 5 <! DOCTYPE html > 6 < html > 7 < head > 8 < title > Arquivo não encontrado ! </ title > 9 </ head > 10 < body > 11 < h2 > 12 Esse arquivo não foi encontrado . Verifique se a url está correta . 13 </ h2 > 14 </ body > 15 </ html > Código CSHTML 11.5: NotFound.cshtml 8 Altere o arquivo Web.Config para definir que quando um erro 404 ocorrer, a página exibida deve ser aquela que acabamos de criar. 1 ... 2 < system . web > 3 < customErrors mode = " On " > 4 < error statusCode = " 404 " redirect = " ~/ ErrorPage / NotFound " / > 5 </ customErrors > 6 ... 7 < system . web > 8 ... Código XML 11.4: Web.config Tente acessar páginas da aplicação que não existam. Acesse, por exemplo, o endereço http: //localhost:<PORTA_APP>/PaginaInexistente. www.k19.com.br 137
  • 148.
    T RATAMENTO DEE RROS 138 138 www.k19.com.br
  • 149.
    APÊNDICE ASP.NET W EB API A Para permitir a implementação de webservices restful, o ASP.NET Web API foi adicionado à quarta versão do ASP .NET MVC. Webservices restful são baseados no estilo arquitetural REST. Discutiremos, a seguir, os principais conceitos desse estilo arquitetural. REST Resources No estilo arquitetural REST, toda informação disponível é chamada de resource. Por exemplo, uma imagem é um resource. Uma lista de produtos é um resource. O cadastro de uma pessoa é um resource. URIs Todo resource possui um identificador único globalmente. Os identificadores são utililzados para acessar os respectivos resources. Particularmente, em uma rede HTTP, os resources são identificados por URIs(Uniform Resource Identifier - http://tools.ietf.org/html/rfc3986). Por exemplo, a URI www.k19.com.br/cursos identifica na internet a lista de cursos da K19. Media Types Um resource pode ser apresentado em diversos formatos. Na arquitetura REST, esses formatos são chamados de media type. Considere o cadastro de uma pessoa disponível em uma rede HTTP . Eventualmente, esse cadastro pode ser apresentado em html, xml ou json. 1 < html > 2 < head > 3 < title > Rafael Cosentino </ title > 4 < head > 5 6 < body > 7 < h1 > Rafael Cosentino </ h1 > 8 <p > Líder de treinamentos da K19 </ p > 9 </ body > 10 </ html > 1 < pessoa > 2 < nome > Rafael Cosentino </ nome > 3 < descricao > Líder de treinamentos da K19 </ descricao > 4 < pessoa > www.k19.com.br 139
  • 150.
    ASP.NET W EBAPI 140 1 { " nome " : " Rafael Cosentino " , " descricao " : " Líder de treinamentos da K19 " } Operações Em uma arquitetura REST, um conjunto pequeno e fixo de operações deve ser definido previa- mente. As operações são utilizadas para manipular os recursos de alguma forma. Por exemplo, em uma rede HTTP os recursos são manipulados pelos métodos do protocolo , HTTP. Podemos atribuir uma semântica diferente para cada um desses métodos. Resource Método HTTP Semântica www.k19.com.br/cursos GET pega a lista de cursos www.k19.com.br/cursos POST adiciona um curso na lista Resources No ASP.NET Web API, os resources são definidos por controladores de uma aplicação ASP.NET MVC que derivam de System.Web.Http.ApiController. 1 public class CursosController : ApiController 2 { 3 ... 4 } Código C# A.1: CursosController.cs URIs As URIs dos resources são definidas através de rotas do ASP.NET Web API criadas no arquivo RouteConfig.cs. O Visual Web Developer adiciona a seguinte rota padrão do ASP.NET Web API nos projetos ASP.NET MVC 4: 1 routes . MapHttpRoute ( 2 name : " DefaultApi " , 3 routeTemplate : " api /{ controller }/{ id } " , 4 defaults : new { id = RouteParameter . Optional } 5 ); Código C# A.2: RouteConfig.cs De acordo com a rota padrão do ASP.NET Web API, a URI do resource correspondente ao contro- lador Cursos é http://localhost:<PORTA_APP>/api/cursos. 140 www.k19.com.br
  • 151.
    141 ASP.NET W EB API Operações Podemos associar as operações HTTP aos métodos da classe CursosController. Essas associa- ções são estabelecidas automaticamente através dos nomes dos métodos. Por exemplo, a operação GET do HTTP é associada automaticamente a um método cujo nome possui o prefixo Get. Veja o exemplo abaixo. 1 public class CursosController : ApiController 2 { 3 ... 4 // GET http :// localhost : < PORTA_APP >/ api / cursos 5 public Curso [] GetCursos () 6 { 7 ... 8 } 9 ... 10 } Código C# A.3: CursosController.cs Na rota padrão do ASP .NET Web API, um parâmetro opcional chamado id foi definido. Quando esse parâmetro estiver presente em uma requisição do tipo GET, podemos recuperar o valor dele em um método cujo nome possua o prefixo Get e tenha um argumento chamado id. Observe o código abaixo. 1 public class CursosController : ApiController 2 { 3 ... 4 // GET http :// localhost : < PORTA_APP >/ api / cursos /5 5 public Curso [] GetCurso ( int id ) 6 { 7 ... 8 } 9 ... 10 } Código C# A.4: CursosController.cs Também podemos recuperar parâmetros de URL. Para isso, basta definir argumentos com os mesmos nomes desses parâmetros nos métodos associados às operações do HTTP. Veja o exemplo abaixo 1 public class CursosController : ApiController 2 { 3 ... 4 // GET http :// localhost : < PORTA_APP >/ api / cursos ? sigla = K32 5 public Curso [] GetCurso ( string sigla ) 6 { 7 ... 8 } 9 ... 10 } Código C# A.5: CursosController.cs Considere o código a seguir. 1 public class CursosController : ApiController www.k19.com.br 141
  • 152.
    ASP.NET W EBAPI 142 2 { 3 ... 4 // POST http :// localhost : < PORTA_APP >/ api / cursos 5 public void PostCurso ( Curso curso ) 6 { 7 ... 8 } 9 ... 10 } Código C# A.6: CursosController.cs O método PostCurso() será associado a operação POST do HTTP. Os valores dos parâmetros en- viados dentro de uma requisição do tipo POST à url http://localhost:<PORTA_APP>/api/cursos serão armazenados automaticamente pelo ASP.NET Web API nas propriedades do argumento curso do método PostCurso() de acordo com o nome dessas propriedades e dos nomes dos parâmetros enviados. Por exemplo, o valor do parâmetro Nome será armazenado dentro da propriedade Nome caso ambos existam. Media Type Por padrão, o ASP NET Web API utiliza os headers Content-type e Accept para definir o media type dos dados de entrada e saída. No exemplo anterior, se uma requisição do tipo GET for realizada à url //POSThttp://localhost:<PORTA_APP>/api/cursos com header Accept: application/json, o resource correspondente será apresentado em formato JSON. Exercícios de Fixação 1 Vamos definir o nosso primeiro webservice utilizando web api. Para isso, adicione uma classe para modelar cursos na pasta Models. 1 namespace K19 . Models 2 { 3 public class Curso 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public int CargaHoraria { get ; set ; } 8 } 9 } Código C# A.7: Curso.cs 2 Agora crie um controlador chamado Cursos para implementar as operações do webservice. Na criação desse controlador, selecione o template Empty API Controller. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Net ; 5 using System . Net . Http ; 6 using System . Web . Http ; 7 using LivrariaVirtual . Models ; 8 using System . Web . Mvc ; 9 142 www.k19.com.br
  • 153.
    143 ASP.NET W EB API 10 namespace K19 . Controllers 11 { 12 public class CursosController : ApiController 13 { 14 private static List < Curso > cursos = new List < Curso > 15 { 16 new Curso { 17 Id = 1 , 18 Sigla = " K31 " , 19 Nome = " C # e Orientação a Objetos " 20 }, 21 new Curso { 22 Id = 2 , 23 Sigla = " K32 " , 24 Nome = " Desenvolvimento Web com ASP . NET MVC " } 25 }; 26 27 public List < Curso > Get () 28 { 29 return cursos ; 30 } 31 32 public Curso Get ( string sigla ) 33 { 34 var consulta = from c in cursos 35 where c . Sigla . Equals ( sigla ) 36 select c ; 37 38 if ( consulta . Count () == 0) 39 { 40 return null ; 41 } 42 else 43 { 44 return consulta . First () ; 45 } 46 47 } 48 49 public Curso Get ( int id ) 50 { 51 var consulta = from c in cursos 52 where c . Id == id 53 select c ; 54 55 if ( consulta . Count () == 0) 56 { 57 return null ; 58 } 59 else 60 { 61 return consulta . First () ; 62 } 63 } 64 65 public void Post ( Curso curso ) 66 { 67 cursos . Add ( curso ) ; 68 } 69 70 } 71 } Código C# A.8: CursosController.cs 3 Para testar o webservice que retorna a lista de cursos, basta acessar o seguinte endereço: http: www.k19.com.br 143
  • 154.
    ASP.NET W EBAPI 144 //localhost:<PORTA_APP>/api/cursos. 4 Teste o webservice que devolve o curso a partir de uma sigla. Basta acessar o seguinte endereço: http://localhost:<PORTA_APP>/api/cursos?sigla=K31. 5 Teste o webservice que devolve um curso a partir de um id. Basta acessar o seguinte endereço: http://localhost:<PORTA_APP>/api/cursos/1. 6 Defina uma página com um formulário para testar o webservice que adiciona curso. Para isso, crie um controlador TestaWebServiceCurso conforme o código abaixo: 1 using System . Web . Mvc ; 2 3 namespace K19 . Controllers 4 { 5 public class TestaWebServiceCursoController : Controller 6 { 7 public ActionResult Formulario () 8 { 9 return View () ; 10 } 11 } 12 } Código C# A.9: TestaWebServiceCursoController.cs 7 Para testar o webservice que adiciona curso, devemos definir a página Formulario.cshtml. 1 @model K19 . Models . Curso 2 @{ 3 ViewBag . Title = " Formulario " ; 4 } 5 6 < h2 > Formulario </ h2 > 7 8 @using ( Html . BeginForm ( null , null , FormMethod . Post , new { @action = " / api / cursos " }) ) 9 { 10 < fieldset > 11 < legend > Curso </ legend > 12 < div class = " editor - label " > 13 @Html . LabelFor ( model = > model . Id ) 14 </ div > 15 < div class = " editor - field " > 16 @Html . EditorFor ( model = > model . Id ) 17 </ div > 18 < div class = " editor - label " > 19 @Html . LabelFor ( model = > model . Sigla ) 20 </ div > 21 < div class = " editor - field " > 22 @Html . EditorFor ( model = > model . Sigla ) 23 </ div > 24 < div class = " editor - label " > 25 @Html . LabelFor ( model = > model . Nome ) 26 </ div > 27 < div class = " editor - field " > 28 @Html . EditorFor ( model = > model . Nome ) 29 </ div > 30 <p > 31 < input type = " submit " value = " Create " / > 32 </ p > 33 </ fieldset > 34 } 35 @section Scripts { 36 @Scripts . Render ( " ~/ bundles / jqueryval " ) 37 } 144 www.k19.com.br
  • 155.
    145 ASP.NET W EB API Código CSHTML A.1: Formulario.cshtml 8 Teste o webservice que adiciona curso através do formulário criado no exercício anterior. Acesse o formulário através do seguinte endereço: http://localhost:<PORTA_APP>/TestaWebServiceCurso/ Formulario. Para verificar se o curso foi adicionado, acesse http://localhost:<PORTA_APP>/api/ cursos/. www.k19.com.br 145
  • 156.
    ASP.NET W EBAPI 146 146 www.k19.com.br
  • 157.
    APÊNDICE MIGRATIONS B O Entity Framework Code First da Microsoft tem uma funcionalidade que permite controlar as mudanças no banco de dados que serão realizadas de acordo com as alterações na camada de mo- delo da aplicação. Essa funcionalidade é chamada de Code First Migrations. O Code First Migrations foi adicionado na versão 4.3.1 do Entity Framework. Neste capítulo uti- lizaremos o Entity Framework 5.0 RC. Passo a Passo Para testar o funcionamento do Code First Migrations, criaremos um projeto no Visual Studio. Agora, devemos instalar o Entity Framework 5.0 RC. Para isso, utilizaremos o gerenciador de pa- cotes Nuget do Visual Studio. www.k19.com.br 147
  • 158.
    M IGRATIONS 148 O Nuget permite que bibliotecas e ferramentas sejam instaladas no Visual Studio. Ao instalar pacotes no seu projeto, ele adiciona as bibliotecas, referências e faz as alterações necessárias nos ar- quivos de configuração. Mais informações sobre o Nuget e os pacotes disponíveis, acesse o endereço http://nuget.org/packages Para instalar o Entity Framework 5.0 RC, basta executar o seguinte comando no Package Manager Console. Install-Package EntityFramework -Pre Após a instalação do Entity Framework 5.0 RC, adicionaremos uma classe de modelo chamada Editora. Essa entidade será mapeada através de uma classe chamada Livraria. Para testar, criare- mos uma classe com o método Main(). Observe o código dessas três classes. 1 namespace EFMigrations 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 148 www.k19.com.br
  • 159.
    149 M IGRATIONS 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 } 9 } Código C# B.1: Editora.cs 1 namespace EFMigrations 2 { 3 public class Livraria : DbContext 4 { 5 public DbSet < Editora > Editoras { get ; set ; } 6 } 7 } Código C# B.2: Livraria.cs 1 namespace EFMigrations 2 { 3 class Program 4 { 5 static void Main ( string [] args ) 6 { 7 using ( Livraria livraria = new Livraria () ) 8 { 9 Editora e = new Editora { Nome = " K19 " , Email = " contato@k19 . com . br " }; 10 livraria . Editoras . Add ( e ) ; 11 livraria . SaveChanges () ; 12 } 13 } 14 } 15 } Código C# B.3: Program.cs Após a execução do projeto, teremos a seguinte tabela e banco de dados. O próximo passo é alterar a classe de modelo Editora. 1 namespace EFMigrations 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 www.k19.com.br 149
  • 160.
    M IGRATIONS 150 9 public string Telefone { get ; set ; } 10 } 11 } Código C# B.4: Editora.cs Com a alteração na classe Editora, um exeção é gerada ao executar o projeto. Para resolver esse problema, o banco de dados precisa ser atualizado. Para fazer essa atualização, o Entity Framework recomenda a utilização da ferramenta Code First Migrations. O primeiro passo para utilizar Code First Migrations é habilitá-lo e adicioná-lo ao projeto através do Package Manager Console. O comando visto acima adiciona uma pasta chamada Migrations no projeto. Esta pasta contém dois arquivos. • A classe Configuration permite definir o comportamento do Code First Migrations para o nosso contexto. • A classe parcial InitialCreate define a primeira versão das tabelas do banco de dados. O Code First Migration tem dois comandos fundamentais. • Add-Migration que gera o código necessário para atualizar o banco de dados de acordo com as alterações nas classes de modelo. 150 www.k19.com.br
  • 161.
    151 M IGRATIONS • Update-Database aplica as alterações pendentes no banco de dados. Como adicionamos a propriedade Telefone na classe de modelo Editora, devemos criar e exe- cutar uma migração para atualizar o banco de dados. Para criar uma migração, devemos utilizar o comando Add-Migration. Para executar a migração, devemos utilizar o comando Update-Database. Observe a execução do comando Add-Migration. A classe que define a migração é adicionada na pasta Migrations Para aplicar a migração, devemos executar o comando Update-Database. No banco de dados, uma coluna é adicionada na tabela Editoras. www.k19.com.br 151
  • 162.
    M IGRATIONS 152 Podemos também adicionar uma nova entidade. Considere a seguinte classe para definir os li- vros da nossa aplicação. 1 namespace EFMigrations 2 { 3 public class Livro 4 { 5 public int Id { get ; set ; } 6 public string Titulo { get ; set ; } 7 public decimal Preco { get ; set ; } 8 public int EditoraId { get ; set ; } 9 public Editora Editora { get ; set ; } 10 } 11 } Código C# B.5: Livro.cs Para estabelecer o relacionamento entre editoras e livros, a classe Editora deve ser alterada. 1 namespace EFMigrations 2 { 3 public class Editora 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Email { get ; set ; } 8 public string Telefone { get ; set ; } 9 10 [ ForeignKey ( " EditoraId " ) ] 11 public virtual IList < Livro > Livros { get ; set ; } 12 } 13 } Código C# B.6: Editora.cs A classe livro deve ser mapeada no contexto Livraria. 1 namespace EFMigrations 2 { 3 public class Livraria : DbContext 4 { 5 public DbSet < Editora > Editoras { get ; set ; } 6 public DbSet < Livro > Livros { get ; set ; } 7 } 8 } 152 www.k19.com.br
  • 163.
    153 M IGRATIONS Código C# B.7: Livraria.cs Após a alteração das classes Editora e Livraria e a criação da entidade Livro, devemos criar uma migração para atualizar o banco de dados. Para aplicar a migração, devemos utilizar o comando Update-Database. A tabela correspondente à classe Livro e a chave estrangeira para realizar o relacionamento entre livros e editoras são adicionadas no banco de dados. www.k19.com.br 153
  • 164.
    M IGRATIONS 154 Exercícios de Fixação 1 Para testar o Code First Migrations, altere a classe Editora do projeto K19. Acrescente nessa classe uma propriedade chamada Telefone. 1 using System . ComponentModel . DataAnnotations ; 2 3 namespace K19 . Models 4 { 5 public class Editora 6 { 7 public int Id { get ; set ; } 8 9 [ Required ( ErrorMessage = " O campo nome é obrigatório " ) ] 10 public string Nome { get ; set ; } 11 12 [ Required ( ErrorMessage = " O campo email é obrigatório " ) ] 13 public string Email { get ; set ; } 14 15 public string Telefone { get ; set ; } 16 } 17 } Código C# B.8: Editora.cs 2 Acesse o seguinte endereço: http://localhost:<PORTA_APP>/Editora. Verifique que uma exceção do tipo System.InvalidOperationException ocorrerá. 154 www.k19.com.br
  • 165.
    155 M IGRATIONS 3 Para corrigir o problema visto no exercício anterior, devemos habilitar o Code First Migrations. Para isso, execute o seguinte comando através do Package Manager Console. Enable-Migrations. www.k19.com.br 155
  • 166.
    M IGRATIONS 156 Verifique que uma pasta chamada Migrations com dois arquivos: <DATA>_InitialCreate.cs e Con- figuration.cs foi gerada no projeto K19. 4 Após habilitar o Code First Migrations no projeto K19, devemos adicionar uma migração para atualizar o banco de dados adicionando a coluna Telefone na tabela Editoras. Crie uma migração chamada AddTelefoneToEditora através do comando Add-Migration. Execute este comando através do Package Manager Console. Verifique que um arquivo de migração foi criado na pasta Migrations: 1 namespace K19 . Migrations 2 { 3 using System ; 4 using System . Data . Entity . Migrations ; 5 6 public partial class AddTelefoneToEditora : DbMigration 7 { 8 public override void Up () 9 { 10 AddColumn ( " dbo . Editoras " , " Telefone " , c = > c . String () ) ; 11 } 12 13 public override void Down () 156 www.k19.com.br
  • 167.
    157 M IGRATIONS 14 { 15 DropColumn ( " dbo . Editoras " , " Telefone " ) ; 16 } 17 } 18 } Código C# B.9: <DATA>_AddTelefoneToEditora.cs 5 Para atualizar a tabela Editoras no banco de dados, utilize o comando Update-Database que é executado através do Package Manager Console. Verifique que a coluna Telefone foi adicionada a tabela Editoras. 6 Defina uma classe Livro na pasta Models conforme o código abaixo: 1 namespace K19 . Models 2 { 3 public class Livro 4 { 5 public int Id { get ; set ; } 6 public string Titulo { get ; set ; } 7 public decimal Preco { get ; set ; } 8 public int EditoraId { get ; set ; } 9 public Editora Editora { get ; set ; } 10 } www.k19.com.br 157
  • 168.
    M IGRATIONS 158 11 } Código C# B.10: Livro.cs 7 Altere a classe K19Context para mapear a classe Livro para uma tabela no banco de dados. 1 using System . Data . Entity ; 2 3 namespace K19 . Models 4 { 5 public class K19Context : DbContext 6 { 7 public DbSet < Editora > Editoras { get ; set ; } 8 public DbSet < Livro > Livros { get ; set ; } 9 } 10 } Código C# B.11: K19Context.cs 8 Para gerar a tabela Livros no banco de dados, devemos criar uma migração. Crie uma migração chamada AddLivro através do comando Add-Migration. 9 Execute o comando Update-Database através do Package Manager Console para gerar a tabela referente a classe Livro no banco de dados. 158 www.k19.com.br
  • 169.
    159 M IGRATIONS Verifique que a tabela referente a classe Livro foi gerada no banco de dados. www.k19.com.br 159
  • 170.
    M IGRATIONS 160 160 www.k19.com.br
  • 171.
    APÊNDICE P ROJETO C Nos capítulos anteriores, vimos os recursos do ASP .NET MVC e do Entity Framework. Agora, vamos solidificar os conhecimentos obtidos e, além disso, mostraremos alguns padrões e conceitos relacionados ao desenvolvimento de aplicações web. Como exemplo de aplicação desenvolveremos uma aplicação de cadastro de jogadores e seleções de futebol. Modelo Por onde começar o desenvolvimento de uma aplicação? Essa é uma questão recorrente. Um ótimo ponto de partida é desenvolver as entidades principais da aplicação. No nosso caso, vamos nos res- tringir às entidades Selecao e Jogador. Devemos estabelecer um relacionamento entre essas entida- des já que um jogador atua em uma seleção. Exercícios de Fixação 1 Crie um projeto do tipo ASP .NET MVC 4 Web Application chamado K19-CopaDoMundo se- guindo os passos vistos no exercício do capítulo 4. 2 Adicione na pasta Models as seguintes classes. 1 namespace K19CopaDoMundo . Models 2 { 3 public class Selecao 4 { 5 public int Id { get ; set ; } 6 public string Pais { get ; set ; } 7 public string Tecnico { get ; set ; } 8 } 9 } Código C# C.1: Selecao.cs 1 namespace K19CopaDoMundo . Models 2 { 3 public class Jogador 4 { 5 public int Id { get ; set ; } 6 public string Nome { get ; set ; } 7 public string Posicao { get ; set ; } 8 public DateTime Nascimento { get ; set ; } 9 public double Altura { get ; set ; } 10 public int SelecaoId { get ; set ; } 11 public Selecao Selecao { get ; set ; } 12 } 13 } www.k19.com.br 161
  • 172.
    P ROJETO 162 Código C# C.2: Jogador.cs Persistência - Mapeamento Depois de definir algumas entidades podemos começar o processo de implementação da persis- tência da nossa aplicação. Vamos aplicar os recursos do Entity Framework - Code First que apren- demos nos primeiros capítulos. Inicialmente, vamos definir o mapeamento das nossas entidades através de uma classe derivada de DbContext e acrescentar as propriedades referentes a chave pri- mária e chave estrangeira. Exercícios de Fixação 3 Adicione as seguintes propriedades e anotações as classes Selecao e Jogador. 1 using System . ComponentModel . DataAnnotations . Schema ; 2 3 namespace K19CopaDoMundo . Models 4 { 5 [ Table ( " Selecoes " ) ] 6 public class Selecao 7 { 8 public int Id { get ; set ; } 9 public string Pais { get ; set ; } 10 public string Tecnico { get ; set ; } 11 public virtual List < Jogador > Jogadores { get ; set ; } 12 } 13 } Código C# C.3: Selecao.cs 1 using System . ComponentModel . DataAnnotations . Schema ; 2 3 namespace K19CopaDoMundo . Models 4 { 5 [ Table ( " Jogadores " ) ] 6 public class Jogador 7 { 8 public int Id { get ; set ; } 9 public string Nome { get ; set ; } 10 public string Posicao { get ; set ; } 11 public DateTime Nascimento { get ; set ; } 12 public double Altura { get ; set ; } 13 public int SelecaoId { get ; set ; } 14 [ InverseProperty ( " Jogadores " ) ] 15 public virtual Selecao Selecao { get ; set ; } 16 } 17 } Código C# C.4: Jogador.cs 4 Adicione a classe K19CopaDoMundoContext a pasta Models. 162 www.k19.com.br
  • 173.
    163 C.3. P ERSISTÊNCIA - M APEAMENTO 1 using System . Data . Entity ; 2 3 namespace K19CopaDoMundo . Models 4 { 5 public class K19CopaDoMundoContext : DbContext 6 { 7 public DbSet < Selecao > Selecoes { get ; set ; } 8 public DbSet < Jogador > Jogadores { get ; set ; } 9 } 10 } Código C# C.5: K19CopaDoMundoContext.cs Persistência - Configuração Precisamos definir a nossa string de conexão para que a nossa aplicação utilize a base de dados k19copadomundo como padrão. Exercícios de Fixação 5 Acrescente ao arquivo Web.config, que fica na raiz do projeto, a string de conexão. 1 < connectionStrings > 2 < add 3 name = " K19CopaDoMundoContext " providerName = " System . Data . SqlClient " 4 connectionString = " Server =. SQLEXPRESS ; Database = k19copadomundo ; 5 User Id = sa ; Password = sa ; Trusted_Connection = False ; MultipleActiveResultSets = True " / > 6 </ connectionStrings > Código XML C.1: Web.config Persistência - Repositórios Vamos deixar os repositórios para acessar as entidades da nossa aplicação preparados. Os repositó- rios precisam de DbContexts para realizar as operações de persistência. Então, cada repositório terá um construtor para receber um DbContext como parâmetro. Exercícios de Fixação 6 Crie uma classe na pasta Models chamada SelecaoRepository. 1 using System ; 2 using System . Collections . Generic ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 public class SelecaoRepository : IDisposable 7 { 8 private bool disposed = false ; 9 10 private K19CopaDoMundoContext context ; 11 12 public SelecaoRepository ( K19CopaDoMundoContext context ) 13 { www.k19.com.br 163
  • 174.
    P ROJETO 164 14 this . context = context ; 15 } 16 17 public void Adiciona ( Selecao selecao ) 18 { 19 context . Selecoes . Add ( selecao ) ; 20 } 21 22 public List < Selecao > Selecoes 23 { 24 get 25 { 26 return context . Selecoes . ToList () ; 27 } 28 } 29 public void Salva () 30 { 31 context . SaveChanges () ; 32 } 33 34 protected virtual void Dispose ( bool disposing ) 35 { 36 if (! this . disposed ) 37 { 38 if ( disposing ) 39 { 40 context . Dispose () ; 41 } 42 } 43 this . disposed = true ; 44 } 45 46 public void Dispose () 47 { 48 Dispose ( true ) ; 49 GC . SuppressFinalize ( this ) ; 50 } 51 } 52 } Código C# C.6: SelecaoRepository.cs 7 Analogamente crie um repositório de jogadores. 1 using System ; 2 using System . Collections . Generic ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 public class JogadorRepository : IDisposable 7 { 8 private bool disposed = false ; 9 private K19CopaDoMundoContext context ; 10 11 public JogadorRepository ( K19CopaDoMundoContext context ) 12 { 13 this . context = context ; 14 } 15 16 public void Adiciona ( Jogador jogador ) 17 { 18 context . Jogadores . Add ( jogador ) ; 19 } 20 21 public List < Jogador > Jogadores 22 { 23 get { return context . Jogadores . ToList () ; } 24 } 25 164 www.k19.com.br
  • 175.
    165 C.3. P ERSISTÊNCIA - M APEAMENTO 26 public void Salva () 27 { 28 context . SaveChanges () ; 29 } 30 31 protected virtual void Dispose ( bool disposing ) 32 { 33 if (! this . disposed ) 34 { 35 if ( disposing ) 36 { 37 context . Dispose () ; 38 } 39 } 40 this . disposed = true ; 41 } 42 43 public void Dispose () 44 { 45 Dispose ( true ) ; 46 GC . SuppressFinalize ( this ) ; 47 } 48 } 49 } Código C# C.7: JogadorRepository.cs Unit of Work O único propósito de criar uma classe UnitOfWork é ter certeza que quando temos múltiplos repositórios eles compartilham o mesmo DbContext. Para isto, devemos apenas criar um método Salva e uma propriedade para cada repositório. Exercícios de Fixação 8 Crie uma classe UnitOfWork na pasta Models. 1 using System ; 2 using System . Collections . Generic ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 public class UnitOfWork : IDisposable 7 { 8 private bool disposed = false ; 9 private K19CopaDoMundoContext context = new K19CopaDoMundoContext () ; 10 private SelecaoRepository selecaoRepository ; 11 private JogadorRepository jogadorRepository ; 12 13 14 public JogadorRepository JogadorRepository 15 { 16 get 17 { 18 if ( jogadorRepository == null ) 19 { 20 jogadorRepository = new JogadorRepository ( context ) ; 21 } 22 return jogadorRepository ; 23 } 24 } www.k19.com.br 165
  • 176.
    P ROJETO 166 25 26 public SelecaoRepository SelecaoRepository 27 { 28 get 29 { 30 if ( selecaoRepository == null ) 31 { 32 selecaoRepository = new SelecaoRepository ( context ) ; 33 } 34 return selecaoRepository ; 35 } 36 } 37 38 public void Salva () 39 { 40 context . SaveChanges () ; 41 } 42 43 protected virtual void Dispose ( bool disposing ) 44 { 45 if (! this . disposed ) 46 { 47 if ( disposing ) 48 { 49 context . Dispose () ; 50 } 51 } 52 this . disposed = true ; 53 } 54 55 public void Dispose () 56 { 57 Dispose ( true ) ; 58 GC . SuppressFinalize ( this ) ; 59 } 60 } 61 } Código C# C.8: UnitOfWork.cs Apresentação - Template Vamos definir um template para as telas da nossa aplicação. Aplicaremos algumas regras CSS para melhorar a parte visual das telas. Exercícios de Fixação 9 Na pasta Content, altere o arquivo Site.css acrescentando algumas regras CSS. 1 . logo 2 { 3 vertical - align : middle ; 4 } 5 6 . botao 7 { 8 background - color : #064 D83 ; 9 margin : 0 0 0 20 px ; 10 color : white ; 11 text - decoration : none ; 12 font - size : 20 px ; 13 line - height : 20 px ; 14 padding : 5 px ; 166 www.k19.com.br
  • 177.
    167 C.3. P ERSISTÊNCIA - M APEAMENTO 15 vertical - align : middle ; 16 } 17 18 . botao : hover 19 { 20 background - color : # cccccc ; 21 color : #666666; 22 } 23 24 . formulario fieldset 25 { 26 float : left ; 27 margin : 0 0 20 px 0; 28 border : 1 px solid #333333; 29 } 30 31 . formulario fieldset legend 32 { 33 color : #064 D83 ; 34 font - weight : bold ; 35 } 36 37 . botao - formulario 38 { 39 background - color : #064 D83 ; 40 color : # ffffff ; 41 padding : 5 px ; 42 vertical - align : middle ; 43 border : none ; 44 } 45 46 . titulo 47 { 48 color : #064 D83 ; 49 clear : both ; 50 } 51 52 . tabela 53 { 54 border : 1 px solid #064 D83 ; 55 border - collapse : collapse ; 56 } 57 58 . tabela tr th 59 { 60 background - color : #064 D83 ; 61 color : # ffffff ; 62 } 63 64 . tabela tr th , . tabela tr td 65 { 66 border : 1 px solid #064 D83 ; 67 padding : 2 px 5 px ; 68 } 69 70 71 /* Styles for validation helpers 72 -- ------ ------ ------- ------ ------ ------ ------ ------ ------ -- */ 73 . field - validation - error 74 { 75 color : # ff0000 ; 76 } 77 78 . field - validation - valid 79 { 80 display : none ; 81 } 82 83 . input - validation - error 84 { www.k19.com.br 167
  • 178.
    P ROJETO 168 85 border : 1 px solid # ff0000 ; 86 background - color : # ffeeee ; 87 } 88 89 . validation - summary - errors 90 { 91 font - weight : bold ; 92 color : # ff0000 ; 93 } 94 95 . validation - summary - valid 96 { 97 display : none ; 98 } Código CSS C.1: Site.css 10 Copie o arquivo k19-logo.png da pasta K19-Arquivos da sua Área de Trabalho para a pasta Images. 11 Agora altere o arquivo _Layout.cshtml. 1 <! DOCTYPE html > 2 < html lang = " en " > 3 < head > 4 < meta charset = " utf -8 " / > 5 < title > Copa do Mundo </ title > 6 < link href = " ~/ __TemplateIcon . ico " rel = " shortcut icon " type = " image /x - icon " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 9 @Scripts . Render ( " ~/ bundles / modernizr " ) 10 </ head > 11 < body > 12 < header > 13 < div class = " content - wrapper " > 14 < div class = " float - left " > 15 <p class = " site - title " > < img class = " logo " 16 src = " ~/ Images / k19 - logo . jpg " alt = " K19 Logo " / > </ p > </ div > 17 < div class = " float - right " > 18 < nav > 19 < ul id = " menu " > 20 < li > @Html . ActionLink ( " Selecoes " , " Index " , " Selecao " , null , ← new { @class = " botao " }) </ li > 21 < li > @Html . ActionLink ( " Jogadores " , " Index " , " Jogador " , null , new { ← @class = " botao " }) </ li > 22 </ ul > 23 </ nav > 24 </ div > 25 </ div > 26 </ header > 27 < div id = " body " > 28 @RenderSection ( " featured " , required : false ) 29 < section class = " content - wrapper main - content clear - fix " > 30 @RenderBody () 31 </ section > 32 </ div > 33 < footer > 34 < div class = " content - wrapper " > 35 < div class = " float - left " > 36 <p >& copy ; @DateTime . Now . Year - K19 Copa do Mundo </ p > 37 </ div > 38 < div class = " float - right " > 39 < ul id = " social " > 40 < li > <a href = " http :// facebook . com / k19treinamentos " class = " ← facebook " > Facebook - K19 Treinamentos </ a > </ li > 41 < li > <a href = " http :// twitter . com / k19treinamentos " class = " ← twitter " > Twitter - K19 Treinamentos </ a > </ li > 42 </ ul > 168 www.k19.com.br
  • 179.
    169 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 43 </ div > 44 </ div > 45 </ footer > 46 @Scripts . Render ( " ~/ bundles / jquery " ) 47 @RenderSection ( " scripts " , required : false ) 48 </ body > 49 </ html > Código CSHTML C.1: _Layout.cshtml Cadastrando e Listando Seleções Na tela de seleções, vamos adicionar um formulário para cadastrar novas seleções e uma tabela para apresentar as já cadastradas. Aplicaremos regras de validação específicas para garantir que ne- nhum dado incorreto seja armazenado no banco de dados. Exercícios de Fixação 12 Para cadastrar a seleção, devemos definir o controlador. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Data ; 4 using System . Data . Entity ; 5 using System . Linq ; 6 using System . Web ; 7 using System . Web . Mvc ; 8 using K19CopaDoMundo . Models ; 9 10 namespace K19CopaDoMundo . Controllers 11 { 12 public class SelecaoController : Controller 13 { 14 private UnitOfWork unitOfWork = new UnitOfWork () ; 15 16 public ActionResult Create () 17 { 18 return View () ; 19 } 20 21 protected override void Dispose ( bool disposing ) 22 { 23 unitOfWork . Dispose () ; 24 base . Dispose ( disposing ) ; 25 } 26 } 27 } Código C# C.9: SelecaoController.cs 13 Vamos criar uma tela Create.cshtml para cadastrar as seleções. Adicione o arquivo a pasta Views/Selecoes com o seguinte conteúdo. 1 @model K19CopaDoMundo . Models . Selecao 2 3 @{ 4 ViewBag . Title = " Create " ; www.k19.com.br 169
  • 180.
    P ROJETO 170 5 } 6 7 < h2 > Create </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 12 < fieldset > 13 < legend > Selecao </ legend > 14 15 < div class = " editor - label " > 16 @Html . LabelFor ( model = > model . Pais ) 17 </ div > 18 < div class = " editor - field " > 19 @Html . EditorFor ( model = > model . Pais ) 20 @Html . ValidationMessageFor ( model = > model . Pais ) 21 </ div > 22 23 < div class = " editor - label " > 24 @Html . LabelFor ( model = > model . Tecnico ) 25 </ div > 26 < div class = " editor - field " > 27 @Html . EditorFor ( model = > model . Tecnico ) 28 @Html . ValidationMessageFor ( model = > model . Tecnico ) 29 </ div > 30 31 <p > 32 < input type = " submit " value = " Create " / > 33 </ p > 34 </ fieldset > 35 } 36 37 < div > 38 @Html . ActionLink ( " Listagem de Seleções " , " Index " ) 39 </ div > 40 41 @section Scripts { 42 @Scripts . Render ( " ~/ bundles / jqueryval " ) 43 } Código CSHTML C.2: Create.cshtml 14 O próximo passo é definir a action que irá salvar a seleção no nosso banco de dados. Devemos também acrescentar as validações a nossa entidade. 1 [ HttpPost ] 2 public ActionResult Create ( Selecao selecao ) 3 { 4 if ( ModelState . IsValid ) 5 { 6 unitOfWork . SelecaoRepository . Adiciona ( selecao ) ; 7 unitOfWork . Salva () ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 return View ( selecao ) ; 11 } Código C# C.10: SelecaoController.cs 1 using System . ComponentModel . DataAnnotations . Schema ; 2 using System . ComponentModel . DataAnnotations ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 [ Table ( " Selecoes " ) ] 7 public class Selecao 8 { 170 www.k19.com.br
  • 181.
    171 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 9 public int Id { get ; set ; } 10 [ Required ( ErrorMessage = " O campo Pais é obrigatório . " ) ] 11 public string Pais { get ; set ; } 12 [ Required ( ErrorMessage = " O campo Tecnico é 13 obrigatório . " ) ] public string Tecnico { get ; set ; } 14 public virtual List < Jogador > Jogadores { get ; set ; } 15 } 16 } Código C# C.11: Selecao.cs 15 Defina a action e a página para listar todas as entidades de seleção. 1 public ActionResult Index () 2 { 3 return View ( unitOfWork . SelecaoRepository . Selecoes ) ; 4 } Código C# C.12: SelecaoController.cs 1 @model IEnumerable < K19CopaDoMundo . Models . Selecao > 2 3 @{ 4 ViewBag . Title = " Index " ; 5 } 6 7 < h2 > Index </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Create New " , " Create " ) 11 </ p > 12 < table > 13 < tr > 14 < th > 15 @Html . DisplayNameFor ( model = > model . Pais ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Tecnico ) 19 </ th > 20 < th > </ th > 21 </ tr > 22 23 @foreach ( var item in Model ) { 24 < tr > 25 < td > 26 @Html . DisplayFor ( modelItem = > item . Pais ) 27 </ td > 28 < td > 29 @Html . DisplayFor ( modelItem = > item . Tecnico ) 30 </ td > 31 </ tr > 32 } 33 34 </ table > Código CSHTML C.3: Index.cshtml 16 Vamos definir a tela de listagem de Seleções como a página principal do nosso site. Altere a rota padrão no arquivo RouteConfig.cs. 1 routes . MapRoute ( 2 name : " Default " , 3 url : " { controller }/{ action }/{ id } " , 4 defaults : new { controller = " Selecao " , action = " Index " , 5 id = UrlParameter . Optional } ) ; www.k19.com.br 171
  • 182.
    P ROJETO 172 Código C# C.13: RouteConfig.cs Removendo Seleções Vamos acrescentar a funcionalidade de remover seleções. Exercícios de Fixação 17 Acrescente uma coluna na tabela de listagem de seleções. 1 @model IEnumerable < K19CopaDoMundo . Models . Selecao > 2 3 @{ 4 ViewBag . Title = " Index " ; 5 } 6 7 < h2 > Index </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Create New " , " Create " ) 11 </ p > 12 < table > 13 < tr > 14 < th > 15 @Html . DisplayNameFor ( model = > model . Pais ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Tecnico ) 19 </ th > 20 < th > </ th > 21 </ tr > 22 23 @foreach ( var item in Model ) { 24 < tr > 25 < td > 26 @Html . DisplayFor ( modelItem = > item . Pais ) 27 </ td > 28 < td > 29 @Html . DisplayFor ( modelItem = > item . Tecnico ) 30 </ td > 31 < td > 32 @Html . ActionLink ( " Remover " ," Delete " , new { id = item . Id }) 33 </ td > 34 </ tr > 35 } 36 37 </ table > Código CSHTML C.4: Index.cshtml 18 Defina um método Busca na classe SelecaoRepository que retorna uma entidade seleção a partir de um parâmetro id. 1 public Selecao Busca ( int id ) 2 { 3 return context . Selecoes . Find ( id ) ; 4 } 172 www.k19.com.br
  • 183.
    173 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES Código C# C.14: SelecaoRepository.cs 19 Defina uma action Delete que irá mostrar a tela de confirmação de remoção da entidade. 1 public ActionResult Delete ( int id ) 2 { 3 Selecao selecao = unitOfWork . SelecaoRepository . Busca ( id ) ; 4 return View ( selecao ) ; 5 } Código C# C.15: SelecaoController.cs 20 Defina a tela de confirmação de remoção da seleção. 1 @model K19CopaDoMundo . Models . Selecao 2 3 @{ 4 ViewBag . Title = " Delete " ; 5 } 6 7 < h2 > Remoção de Seleção </ h2 > 8 9 < h3 > Você tem certeza que deseja remover esta seleção ? </ h3 > 10 < fieldset > 11 < legend > Selecao </ legend > 12 13 < div class = " display - label " > 14 @Html . DisplayNameFor ( model = > model . Pais ) 15 </ div > 16 < div class = " display - field " > 17 @Html . DisplayFor ( model = > model . Pais ) 18 </ div > 19 20 < div class = " display - label " > 21 @Html . DisplayNameFor ( model = > model . Tecnico ) 22 </ div > 23 < div class = " display - field " > 24 @Html . DisplayFor ( model = > model . Tecnico ) 25 </ div > 26 </ fieldset > 27 @using ( Html . BeginForm () ) { 28 <p > 29 < input type = " submit " value = " Delete " / > | 30 @Html . ActionLink ( " Listagem De Seleções " , " Index " ) 31 </ p > 32 } Código CSHTML C.5: Delete.cshtml 21 Defina um método na classe SelecaoRepository que remove uma entidade seleção a partir de um parâmetro id. 1 public void Remove ( int id ) 2 { 3 Selecao selecao = Busca ( id ) ; 4 context . Selecoes . Remove ( selecao ) ; 5 } Código C# C.16: SelecaoRepository.cs 22 Defina a action que remove a seleção do banco de dados. www.k19.com.br 173
  • 184.
    P ROJETO 174 1 [ HttpPost ] 2 [ ActionName ( " Delete " ) ] 3 public ActionResult DeleteConfirmed ( int id ) 4 { 5 unitOfWork . SelecaoRepository . Remove ( id ) ; 6 unitOfWork . Salva () ; 7 return RedirectToAction ( " Index " ) ; 8 } Código C# C.17: SelecoesController.cs Cadastrando, Listando e Removendo Jogadores Na tela de jogadores, vamos adicionar um formulário para cadastrar novos jogadores e uma tabela para apresentar os já cadastrados. Aplicaremos regras de validação específicas para garantir que ne- nhum dado incorreto seja armazenado no banco de dados. Exercícios de Fixação 23 Para cadastrar o jogador, devemos definir o controlador. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Data ; 4 using System . Data . Entity ; 5 using System . Linq ; 6 using System . Web ; 7 using System . Web . Mvc ; 8 using K19CopaDoMundo . Models ; 9 10 namespace K19CopaDoMundo . Controllers 11 { 12 public class JogadorController : Controller 13 { 14 private UnitOfWork unitOfWork = new UnitOfWork () ; 15 16 public ActionResult Create () 17 { 18 ViewBag . SelecaoId = new SelectList ( unitOfWork . SelecaoRepository . Selecoes , " Id " , ← " Pais " ) ; 19 return View () ; 20 } 21 22 protected override void Dispose ( bool disposing ) 23 { 24 unitOfWork . Dispose () ; 25 base . Dispose ( disposing ) ; 26 } 27 } 28 } Código C# C.18: JogadorController.cs 24 Vamos criar uma tela Create.cshtml para cadastrar os jogadores. Adicione o arquivo a pasta Views/Jogador com o seguinte conteúdo. 1 @model K19CopaDoMundo . Models . Jogador 2 3 @{ 174 www.k19.com.br
  • 185.
    175 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 4 ViewBag . Title = " Create " ; 5 } 6 7 < h2 > Create </ h2 > 8 9 @using ( Html . BeginForm () ) { 10 @Html . ValidationSummary ( true ) 11 12 < fieldset > 13 < legend > Jogador </ legend > 14 15 < div class = " editor - label " > 16 @Html . LabelFor ( model = > model . Nome ) 17 </ div > 18 < div class = " editor - field " > 19 @Html . EditorFor ( model = > model . Nome ) 20 @Html . ValidationMessageFor ( model = > model . Nome ) 21 </ div > 22 23 < div class = " editor - label " > 24 @Html . LabelFor ( model = > model . Posicao ) 25 </ div > 26 < div class = " editor - field " > 27 @Html . EditorFor ( model = > model . Posicao ) 28 @Html . ValidationMessageFor ( model = > model . Posicao ) 29 </ div > 30 31 < div class = " editor - label " > 32 @Html . LabelFor ( model = > model . Nascimento ) 33 </ div > 34 < div class = " editor - field " > 35 @Html . EditorFor ( model = > model . Nascimento ) 36 @Html . ValidationMessageFor ( model = > model . Nascimento ) 37 </ div > 38 39 < div class = " editor - label " > 40 @Html . LabelFor ( model = > model . Altura ) 41 </ div > 42 < div class = " editor - field " > 43 @Html . EditorFor ( model = > model . Altura ) 44 @Html . ValidationMessageFor ( model = > model . Altura ) 45 </ div > 46 47 < div class = " editor - label " > 48 @Html . LabelFor ( model = > model . SelecaoId ) 49 </ div > 50 < div class = " editor - field " > 51 @Html . DropDownList ( " SelecaoId " , String . Empty ) 52 @Html . ValidationMessageFor ( model = > model . SelecaoId ) 53 </ div > 54 55 <p > 56 < input type = " submit " value = " Create " / > 57 </ p > 58 </ fieldset > 59 } 60 61 < div > 62 @Html . ActionLink ( " Listagem de Jogadores " , " Index " ) 63 </ div > 64 65 @section Scripts { 66 @Scripts . Render ( " ~/ bundles / jqueryval " ) 67 } Código CSHTML C.6: Create.cshtml 25 O próximo passo é definir a action que irá salvar o jogador no nosso banco de dados. Devemos também acrescentar as validações a nossa entidade. www.k19.com.br 175
  • 186.
    P ROJETO 176 1 [ HttpPost ] 2 public ActionResult Create ( Jogador jogador ) 3 { 4 if ( ModelState . IsValid ) 5 { 6 unitOfWork . JogadorRepository . Adiciona ( jogador ) ; 7 unitOfWork . Salva () ; 8 return RedirectToAction ( " Index " ) ; 9 } 10 ViewBag . SelecaoId = new SelectList ( unitOfWork . SelecaoRepository . Selecoes , ← " Id " , " Pais " ) ; 11 return View () ; 12 } Código C# C.19: JogadorController.cs 1 using System . ComponentModel . DataAnnotations . Schema ; 2 using System . ComponentModel . DataAnnotations ; 3 4 namespace K19CopaDoMundo . Models 5 { 6 [ Table ( " Jogadores " ) ] 7 public class Jogador 8 { 9 public int Id { get ; set ; } 10 [ Required ( ErrorMessage = " O campo Nome é obrigatório . " ) ] 11 public string Nome { get ; set ; } 12 [ Required ( ErrorMessage = " O campo Posicao é 13 obrigatório . " ) ] public string Posicao { get ; set ; } 14 [ Required ( ErrorMessage = " O campo Nascimento é 15 obrigatório . " ) ] 16 [ DataType ( DataType . Date ) ] 17 public DateTime Nascimento { get ; set ; } 18 [ Required ( ErrorMessage = " O campo Altura é 19 obrigatório . " ) ] 20 public double Altura { get ; set ; } 21 public int SelecaoId { get ; set ; } 22 [ InverseProperty ( " Jogadores " ) ] 23 public virtual Selecao Selecao { get ; set ; } 24 } 25 } Código C# C.20: Jogador.cs 26 Defina a action e a página para listar todas as entidades de jogador. 1 public ActionResult Index () 2 { 3 return View ( unitOfWork . JogadorRepository . Jogadores ) ; 4 } Código C# C.21: JogadorController.cs 1 @model IEnumerable < K19CopaDoMundo . Models . Jogador > 2 3 @{ 4 ViewBag . Title = " Listagem de Jogadores " ; 5 } 6 7 < h2 > Listagem de Jogadores </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Cadastrar Jogador " , " Create " ) 11 </ p > 12 < table > 13 < tr > 176 www.k19.com.br
  • 187.
    177 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 14 < th > 15 @Html . DisplayNameFor ( model = > model . Nome ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Posicao ) 19 </ th > 20 < th > 21 @Html . DisplayNameFor ( model = > model . Nascimento ) 22 </ th > 23 < th > 24 @Html . DisplayNameFor ( model = > model . Altura ) 25 </ th > 26 < th > 27 @Html . DisplayNameFor ( model = > model . SelecaoId ) 28 </ th > 29 < th > </ th > 30 </ tr > 31 32 @foreach ( var item in Model ) { 33 < tr > 34 < td > 35 @Html . DisplayFor ( modelItem = > item . Nome ) 36 </ td > 37 < td > 38 @Html . DisplayFor ( modelItem = > item . Posicao ) 39 </ td > 40 < td > 41 @Html . DisplayFor ( modelItem = > item . Nascimento ) 42 </ td > 43 < td > 44 @Html . DisplayFor ( modelItem = > item . Altura ) 45 </ td > 46 < td > 47 @Html . DisplayFor ( modelItem = > item . SelecaoId ) 48 </ td > 49 </ tr > 50 } 51 52 </ table > Código CSHTML C.7: Index.cshtml Removendo Jogadores Vamos acrescentar a funcionalidade de remover jogadores. Exercícios de Fixação 27 Acrescente uma coluna na tabela de listagem de jogadores. 1 @model IEnumerable < K19CopaDoMundo . Models . Jogador > 2 3 @{ 4 ViewBag . Title = " Listagem de Jogadores " ; 5 } 6 7 < h2 > Listagem de Jogadores </ h2 > 8 9 <p > 10 @Html . ActionLink ( " Cadastrar Jogador " , " Create " ) 11 </ p > 12 < table > 13 < tr > www.k19.com.br 177
  • 188.
    P ROJETO 178 14 < th > 15 @Html . DisplayNameFor ( model = > model . Nome ) 16 </ th > 17 < th > 18 @Html . DisplayNameFor ( model = > model . Posicao ) 19 </ th > 20 < th > 21 @Html . DisplayNameFor ( model = > model . Nascimento ) 22 </ th > 23 < th > 24 @Html . DisplayNameFor ( model = > model . Altura ) 25 </ th > 26 < th > 27 @Html . DisplayNameFor ( model = > model . SelecaoId ) 28 </ th > 29 < th > </ th > 30 </ tr > 31 32 @foreach ( var item in Model ) { 33 < tr > 34 < td > 35 @Html . DisplayFor ( modelItem = > item . Nome ) 36 </ td > 37 < td > 38 @Html . DisplayFor ( modelItem = > item . Posicao ) 39 </ td > 40 < td > 41 @Html . DisplayFor ( modelItem = > item . Nascimento ) 42 </ td > 43 < td > 44 @Html . DisplayFor ( modelItem = > item . Altura ) 45 </ td > 46 < td > 47 @Html . DisplayFor ( modelItem = > item . SelecaoId ) 48 </ td > 49 < td > @Html . ActionLink ( " Remover " , " Delete " , 50 new { id = item . Id }) </ td > </ tr > 51 } 52 </ table > Código CSHTML C.8: Index.cshtml 28 Defina um método Busca na classe JogadorRepository que retorna uma entidade jogador a partir de um parâmetro id. 1 public Jogador Busca ( int id ) 2 { 3 return context . Jogadores . Find ( id ) ; 4 } Código C# C.22: JogadorRepository.cs 29 Defina uma action Delete que irá mostrar a tela de confirmação de remoção da entidade. 1 public ActionResult Delete ( int id ) 2 { 3 Jogador jogador = unitOfWork . JogadorRepository . Busca ( id ) ; 4 return View ( jogador ) ; 5 } Código C# C.23: JogadorController.cs 30 Defina a tela de confirmação de remoção do jogador. 1 @model K19CopaDoMundo . Models . Jogador 178 www.k19.com.br
  • 189.
    179 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 2 3 @{ 4 ViewBag . Title = " Remoção do Jogador " ; 5 } 6 7 < h2 > Remoção do Jogador </ h2 > 8 9 < h3 > Você tem certeza que deseja remover este jogador ? </ h3 > 10 < fieldset > 11 < legend > Jogador </ legend > 12 13 < div class = " display - label " > 14 @Html . DisplayNameFor ( model = > model . Nome ) 15 </ div > 16 < div class = " display - field " > 17 @Html . DisplayFor ( model = > model . Nome ) 18 </ div > 19 20 < div class = " display - label " > 21 @Html . DisplayNameFor ( model = > model . Posicao ) 22 </ div > 23 < div class = " display - field " > 24 @Html . DisplayFor ( model = > model . Posicao ) 25 </ div > 26 27 < div class = " display - label " > 28 @Html . DisplayNameFor ( model = > model . Nascimento ) 29 </ div > 30 < div class = " display - field " > 31 @Html . DisplayFor ( model = > model . Nascimento ) 32 </ div > 33 34 < div class = " display - label " > 35 @Html . DisplayNameFor ( model = > model . Altura ) 36 </ div > 37 < div class = " display - field " > 38 @Html . DisplayFor ( model = > model . Altura ) 39 </ div > 40 41 < div class = " display - label " > 42 @Html . DisplayNameFor ( model = > model . SelecaoId ) 43 </ div > 44 < div class = " display - field " > 45 @Html . DisplayFor ( model = > model . SelecaoId ) 46 </ div > 47 </ fieldset > 48 @using ( Html . BeginForm () ) { 49 <p > 50 < input type = " submit " value = " Delete " / > | 51 @Html . ActionLink ( " Listagem de Jogadores " , " Index " ) 52 </ p > 53 } Código CSHTML C.9: Delete.cshtml 31 Defina um método na classe JogadorRepository que remove uma entidade jogador a partir de um parâmetro id. 1 public void Remove ( int id ) 2 { 3 Jogador jogador = context . Jogadores . Find ( id ) ; 4 context . Jogadores . Remove ( jogador ) ; 5 } Código C# C.24: JogadorRepository.cs 32 Defina a action que remove o jogador do banco de dados. www.k19.com.br 179
  • 190.
    P ROJETO 180 1 [ HttpPost ] 2 [ ActionName ( " Delete " ) ] 3 public ActionResult DeleteConfirmed ( int id ) 4 { 5 unitOfWork . JogadorRepository . Remove ( id ) ; 6 unitOfWork . Salva () ; 7 return RedirectToAction ( " Index " ) ; 8 } Código C# C.25: JogadoresController.cs Membership e Autorização Na maioria dos casos, as aplicações devem controlar o acesso dos usuários. Vamos implementar um mecanismo de autenticação na nossa aplicação utilizando filtro e Membership. As requisições feitas pelos usuários passarão pelo filtro. A função do filtro é verificar se o usuário está logado ou não. Se estiver logado o filtro autoriza o acesso. Caso contrário, o filtro redirecionará o usuário para a tela de login. Exercícios de Fixação 33 Adicione a seguinte classe a pasta Models: 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web . Mvc ; 5 using System . ComponentModel . DataAnnotations ; 6 7 namespace K19CopaDoMundo . Models 8 { 9 10 public class ChangePasswordModel 11 { 12 [ Required ] 13 [ DataType ( DataType . Password ) ] 14 [ Display ( Name = " Senha " ) ] 15 public string OldPassword { get ; set ; } 16 17 [ Required ] 18 [ StringLength (100 , ErrorMessage = " O {0} deve ter no mínimo {2} caracteres . " , ← MinimumLength = 6) ] 19 [ DataType ( DataType . Password ) ] 20 [ Display ( Name = " Nova senha " ) ] 21 public string NewPassword { get ; set ; } 22 23 [ DataType ( DataType . Password ) ] 24 [ Display ( Name = " Confirmação de senha " ) ] 25 [ Compare ( " NewPassword " , ErrorMessage = " A senha e a confirmação não conferem . " ← )] 26 public string ConfirmPassword { get ; set ; } 27 } 28 29 public class LoginModel 30 { 31 [ Required ] 32 [ Display ( Name = " Usuário " ) ] 33 public string UserName { get ; set ; } 34 35 [ Required ] 36 [ DataType ( DataType . Password ) ] 180 www.k19.com.br
  • 191.
    181 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 37 [ Display ( Name = " Senha " ) ] 38 public string Password { get ; set ; } 39 40 [ Display ( Name = " Lembrar ? " ) ] 41 public bool RememberMe { get ; set ; } 42 } 43 44 public class RegisterModel 45 { 46 [ Required ] 47 [ Display ( Name = " Usuário " ) ] 48 public string UserName { get ; set ; } 49 50 [ Required ] 51 [ DataType ( DataType . EmailAddress ) ] 52 [ Display ( Name = " Email " ) ] 53 public string Email { get ; set ; } 54 55 [ Required ] 56 [ StringLength (100 , ErrorMessage = " O {0} deve ter no mínimo {2} caracteres . " , ← MinimumLength = 6) ] 57 [ DataType ( DataType . Password ) ] 58 [ Display ( Name = " Senha " ) ] 59 public string Password { get ; set ; } 60 61 [ DataType ( DataType . Password ) ] 62 [ Display ( Name = " Confirmação de senha " ) ] 63 [ Compare ( " Password " , ErrorMessage = " A senha e a confirmação não conferem . " ) ] 64 public string ConfirmPassword { get ; set ; } 65 } 66 67 } Código C# C.26: Usuario.cs 34 Acrescente a seguinte classe a pasta Controllers. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web ; 5 using System . Web . Mvc ; 6 using System . Web . Security ; 7 using K19CopaDoMundo . Models ; 8 9 namespace K19CopaDoMundo . Controllers 10 { 11 public class UsuarioController : Controller 12 { 13 // 14 // GET : / Usuario / Login 15 16 public ActionResult Login () 17 { 18 return View () ; 19 } 20 21 // 22 // POST : / Usuario / Login 23 24 [ HttpPost ] 25 public ActionResult Login ( LoginModel model , string returnUrl ) 26 { 27 if ( ModelState . IsValid ) 28 { 29 if ( Membership . ValidateUser ( model . UserName , model . Password ) ) 30 { 31 FormsAuthentication . SetAuthCookie ( model . UserName , model . RememberMe ) ; 32 if ( Url . IsLocalUrl ( returnUrl ) && returnUrl . Length > 1 && returnUrl . ← www.k19.com.br 181
  • 192.
    P ROJETO 182 StartsWith ( " / " ) 33 && ! returnUrl . StartsWith ( " // " ) && ! returnUrl . StartsWith ( " / " ) ) 34 { 35 return Redirect ( returnUrl ) ; 36 } 37 else 38 { 39 return RedirectToAction ( " Index " , " Selecao " ) ; 40 } 41 } 42 else 43 { 44 ModelState . AddModelError ( " " , " O usuário e / ou a senha está incorreto . " ) ; 45 } 46 } 47 48 49 return View ( model ) ; 50 } 51 52 // 53 // GET : / Usuario / LogOff 54 55 public ActionResult LogOff () 56 { 57 FormsAuthentication . SignOut () ; 58 59 return Redirect ( " / " ) ; 60 } 61 62 // 63 // GET : / Usuario / Register 64 65 public ActionResult Register () 66 { 67 return View () ; 68 } 69 70 // 71 // POST : / Usuario / Register 72 73 [ HttpPost ] 74 public ActionResult Register ( RegisterModel model ) 75 { 76 if ( ModelState . IsValid ) 77 { 78 // Attempt to register the user 79 MembershipCreateStatus createStatus ; 80 Membership . CreateUser ( model . UserName , model . Password , model . Email , null , null , ← true , null , out createStatus ) ; 81 82 if ( createStatus == MembershipCreateStatus . Success ) 83 { 84 FormsAuthentication . SetAuthCookie ( model . UserName , false /* ← createPersistentCookie */ ) ; 85 return Redirect ( " / " ) ; 86 } 87 else 88 { 89 ModelState . AddModelError ( " " , ErrorCodeToString ( createStatus ) ) ; 90 } 91 } 92 93 94 return View ( model ) ; 95 } 96 97 // 98 // GET : / Usuario / ChangePassword 99 182 www.k19.com.br
  • 193.
    183 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 100 [ Authorize ] 101 public ActionResult ChangePassword () 102 { 103 return View () ; 104 } 105 106 // 107 // POST : / Usuario / ChangePassword 108 109 [ Authorize ] 110 [ HttpPost ] 111 public ActionResult ChangePassword ( ChangePasswordModel model ) 112 { 113 if ( ModelState . IsValid ) 114 { 115 116 117 bool changePasswordSucceeded ; 118 try 119 { 120 MembershipUser currentUser = Membership . GetUser ( User . Identity . Name , true /* ← userIsOnline */ ) ; 121 changePasswordSucceeded = currentUser . ChangePassword ( model . OldPassword , ← model . NewPassword ) ; 122 } 123 catch ( Exception ) 124 { 125 changePasswordSucceeded = false ; 126 } 127 128 if ( changePasswordSucceeded ) 129 { 130 return RedirectToAction ( " ChangePasswordSuccess " ) ; 131 } 132 else 133 { 134 ModelState . AddModelError ( " " , " A senha atual ou a confirmação está incorreta . ← "); 135 } 136 } 137 138 139 return View ( model ) ; 140 } 141 142 // 143 // GET : / Usuario / ChangePasswordSuccess 144 145 public ActionResult ChangePasswordSuccess () 146 { 147 return View () ; 148 } 149 150 private IEnumerable < string > GetErrorsFromModelState () 151 { 152 return ModelState . SelectMany ( x = > x . Value . Errors . Select ( error = > error . ← ErrorMessage ) ) ; 153 } 154 155 # region Status Codes 156 private static string ErrorCodeToString ( MembershipCreateStatus createStatus ) 157 { 158 // See http :// go . microsoft . com / fwlink /? LinkID =177550 for 159 // a full list of status codes . 160 switch ( createStatus ) 161 { 162 case MembershipCreateStatus . DuplicateUserName : 163 return " Este nome de usuário já existe . Defina outro usuário . " ; 164 165 case MembershipCreateStatus . DuplicateEmail : www.k19.com.br 183
  • 194.
    P ROJETO 184 166 return " Este email já foi cadastrado . Defina outro email . " ; 167 168 case MembershipCreateStatus . InvalidPassword : 169 return " Senha incorreta . " ; 170 171 case MembershipCreateStatus . InvalidEmail : 172 return " Email inválido . " ; 173 174 case MembershipCreateStatus . InvalidAnswer : 175 return " Resposta inválida para recuperar a senha . " ; 176 177 case MembershipCreateStatus . InvalidQuestion : 178 return " Questão inválida para recuperar a senha . " ; 179 180 case MembershipCreateStatus . InvalidUserName : 181 return " Usuário inválido . " ; 182 183 case MembershipCreateStatus . ProviderError : 184 return " Ocorreu um erro durante a autenticação . Se o problema persistir , ← contate o administrador . " ; 185 186 case MembershipCreateStatus . UserRejected : 187 return " O cadastro do usuário foi cancelado . Se o problema persistir , ← contate o administrador . " ; 188 189 default : 190 return " Um erro inesperado ocorreu . Se o problema persistir , contate o ← administrador . " ; 191 } 192 } 193 # endregion 194 } 195 196 } Código C# C.27: UsuarioController.cs 35 Crie uma pasta Usuario na pasta Views e acrescente os quatro arquivos abaixo. 1 @model K19CopaDoMundo . Models . ChangePasswordModel 2 3 @{ 4 ViewBag . Title = " Alteração de senha " ; 5 } 6 7 < hgroup class = " title " > 8 < h1 > @ViewBag . Title . </ h1 > 9 < h2 > Utilize este formulário para alterar a sua senha . </ h2 > 10 </ hgroup > 11 12 <p class = " message - info " > 13 Senhas devem ter no mínimo @Membership . MinRequiredPasswordLength caracteres . 14 </ p > 15 16 @using ( Html . BeginForm () ) { 17 @Html . ValidationSummary () 18 19 < fieldset > 20 < legend > Alteração de Senha </ legend > 21 < ol > 22 < li > 23 @Html . LabelFor ( m = > m . OldPassword ) 24 @Html . PasswordFor ( m = > m . OldPassword ) 25 </ li > 26 < li > 27 @Html . LabelFor ( m = > m . NewPassword ) 28 @Html . PasswordFor ( m = > m . NewPassword ) 29 </ li > 30 < li > 184 www.k19.com.br
  • 195.
    185 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 31 @Html . LabelFor ( m = > m . ConfirmPassword ) 32 @Html . PasswordFor ( m = > m . ConfirmPassword ) 33 </ li > 34 </ ol > 35 < input type = " submit " value = " Alterar Senha " / > 36 </ fieldset > 37 } 38 39 @section Scripts { 40 @Scripts . Render ( " ~/ bundles / jqueryval " ) 41 } Código CSHTML C.10: ChangePassword.cshtml 1 @{ 2 ViewBag . Title = " Senha alterada " ; 3 } 4 5 < hgroup class = " title " > 6 < h1 > @ViewBag . Title . </ h1 > 7 < h2 > Sua senha foi alterada com sucesso . </ h2 > 8 </ hgroup > Código CSHTML C.11: ChangePasswordSuccess.cshtml 1 @model K19CopaDoMundo . Models . LoginModel 2 3 @{ 4 ViewBag . Title = " Log in " ; 5 } 6 7 < hgroup class = " title " > 8 < h1 > @ViewBag . Title . </ h1 > 9 < h2 > Formulário de Login </ h2 > 10 </ hgroup > 11 12 @using ( Html . BeginForm ( new { ReturnUrl = ViewBag . ReturnUrl }) ) { 13 @Html . ValidationSummary ( true , " Login não foi efetuado . Informe os dados corretos . " ← ) 14 15 < fieldset > 16 < legend > Formulário de Login </ legend > 17 < ol > 18 < li > 19 @Html . LabelFor ( m = > m . UserName ) 20 @Html . TextBoxFor ( m = > m . UserName ) 21 </ li > 22 < li > 23 @Html . LabelFor ( m = > m . Password ) 24 @Html . PasswordFor ( m = > m . Password ) 25 </ li > 26 < li > 27 @Html . CheckBoxFor ( m = > m . RememberMe ) 28 @Html . LabelFor ( m = > m . RememberMe , new { @class = " checkbox " }) 29 </ li > 30 </ ol > 31 < input type = " submit " value = " Log in " / > 32 </ fieldset > 33 <p > 34 @Html . ActionLink ( " Registrar " , " Register " ) . 35 </ p > 36 } 37 38 @section Scripts { 39 @Scripts . Render ( " ~/ bundles / jqueryval " ) 40 } Código CSHTML C.12: Login.cshtml www.k19.com.br 185
  • 196.
    P ROJETO 186 1 @model K19CopaDoMundo . Models . RegisterModel 2 3 @{ 4 ViewBag . Title = " Cadastro " ; 5 } 6 7 < hgroup class = " title " > 8 < h1 > @ViewBag . Title . </ h1 > 9 < h2 > Cadastrar </ h2 > 10 </ hgroup > 11 12 <p class = " message - info " > 13 Senhas devem ter no minimo @Membership . MinRequiredPasswordLength caracteres . 14 </ p > 15 16 @using ( Html . BeginForm () ) { 17 @Html . ValidationSummary () 18 19 < fieldset > 20 < legend > Cadastro </ legend > 21 < ol > 22 < li > 23 @Html . LabelFor ( m = > m . UserName ) 24 @Html . TextBoxFor ( m = > m . UserName ) 25 </ li > 26 < li > 27 @Html . LabelFor ( m = > m . Email ) 28 @Html . TextBoxFor ( m = > m . Email ) 29 </ li > 30 < li > 31 @Html . LabelFor ( m = > m . Password ) 32 @Html . PasswordFor ( m = > m . Password ) 33 </ li > 34 < li > 35 @Html . LabelFor ( m = > m . ConfirmPassword ) 36 @Html . PasswordFor ( m = > m . ConfirmPassword ) 37 </ li > 38 </ ol > 39 < input type = " submit " value = " Cadastrar " / > 40 </ fieldset > 41 } 42 43 @section Scripts { 44 @Scripts . Render ( " ~/ bundles / jqueryval " ) 45 } Código CSHTML C.13: Register.cshtml 36 Adicione o seguinte partial View _LoginPartial.cshtml a pasta Shared. 1 @if ( Request . IsAuthenticated ) { 2 <p > 3 Olá , @Html . ActionLink ( User . Identity . Name , " ChangePassword " , " Usuario " , ← routeValues : null , htmlAttributes : new { @class = " username " , title = " ← Alterar senha " }) ! 4 @Html . ActionLink ( " Sair " , " LogOff " , " Usuario " ) 5 </ p > 6 } else { 7 < ul > 8 < li > @Html . ActionLink ( " Cadastrar " , " Register " , " Usuario " , routeValues : null , ← htmlAttributes : new { id = " registerLink " }) </ li > 9 < li > @Html . ActionLink ( " Entrar " , " Login " , " Usuario " , routeValues : null , ← htmlAttributes : new { id = " loginLink " }) </ li > 10 </ ul > 11 } Código CSHTML C.14: _LoginPartial.cshtml 186 www.k19.com.br
  • 197.
    187 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 37 Altere o arquivo _Layout.cshtml. 1 <! DOCTYPE html > 2 < html lang = " en " > 3 < head > 4 < meta charset = " utf -8 " / > 5 < title > Copa do Mundo </ title > 6 < link href = " ~/ __TemplateIcon . ico " rel = " shortcut icon " type = " image /x - icon " / > 7 < meta name = " viewport " content = " width = device - width " / > 8 @Styles . Render ( " ~/ Content / themes / base / css " , " ~/ Content / css " ) 9 @Scripts . Render ( " ~/ bundles / modernizr " ) 10 </ head > 11 < body > 12 < header > 13 < div class = " content - wrapper " > 14 < div class = " float - left " > 15 <p class = " site - title " > < img class = " logo " src = " ~/ Images / k19 - logo . jpg ← " alt = " K19 Logo " / > </ p > 16 </ div > 17 < div class = " float - right " > 18 < section id = " login " > 19 @Html . Partial ( " _LoginPartial " ) 20 </ section > 21 < nav > 22 < ul id = " menu " > 23 < li > @Html . ActionLink ( " Selecoes " , " Index " , " Selecao " , null , ← new { @class = " botao " }) </ li > 24 < li > @Html . ActionLink ( " Jogadores " , " Index " , " Jogador " , ← null , new { @class = " botao " }) </ li > 25 </ ul > 26 </ nav > 27 </ div > 28 </ div > 29 </ header > 30 < div id = " body " > 31 @RenderSection ( " featured " , required : false ) 32 < section class = " content - wrapper main - content clear - fix " > 33 @RenderBody () 34 </ section > 35 </ div > 36 < footer > 37 < div class = " content - wrapper " > 38 < div class = " float - left " > 39 <p >& copy ; @DateTime . Now . Year - K19 Copa do Mundo </ p > 40 </ div > 41 < div class = " float - right " > 42 < ul id = " social " > 43 < li > <a href = " http :// facebook . com / k19treinamentos " class = " ← facebook " > Facebook - K19 Treinamentos </ a > </ li > 44 < li > <a href = " http :// twitter . com / k19treinamentos " class = " ← twitter " > Twitter - K19 Treinamentos </ a > </ li > 45 </ ul > 46 </ div > 47 </ div > 48 </ footer > 49 @Scripts . Render ( " ~/ bundles / jquery " ) 50 @RenderSection ( " scripts " , required : false ) 51 </ body > 52 </ html > Código CSHTML C.15: _Layout.cshtml www.k19.com.br 187
  • 198.
    P ROJETO 188 Adicionando um Usuário Administrador com ASP .NET Configuration Antes de definir o filtro Authorize nos controladores de nosso site, vamos criar um usuário com acesso. A maneira mais fácil de criar o usuário é através do ASP .NET Configuration. Exercícios de Fixação 38 Execute o ASP .NET Configuration que fica na aba “Solution Explorer” do Visual Studio. 39 Isto executará um ambiente de configuração. Abra a aba “Security” e clique no link “Enable roles”. 40 Posteriormente, clique em “Create or manage roles”. 41 Defina um role “Administrador” e clique Add Role. 42 Clique no botão “back” e crie um usuário. 43 Defina um usuário admin e senha admink19!. Autorização Role-based Podemos restringir o acesso as páginas com o filtro Authorize e podemos especificar o role que o usuário precisa ter para ter acesso a página. Exercícios de Fixação 44 Altere o filtro de autenticação no Web.config para redirecionar o usuário para a action Login do controlador Usuario. 1 < authentication mode = " Forms " > 2 < forms loginUrl = " ~/ Usuario / Login " timeout = " 2880 " / > 3 </ authentication > Código XML C.2: Web.config 45 Acrescente a seguinte string de conexão no arquivo Web.config para definir o local que as infor- mações dos usuários serão armazenadas (No nosso caso, teremos duas strings de conexão). 1 < connectionStrings > 2 < add 3 name = " K19CopaDoMundoContext " providerName = " System . Data . SqlClient " 4 connectionString = " Server =. SQLEXPRESS ; Database = k19copadomundo ; 5 User Id = sa ; Password = sa ; Trusted_Connection = False ; MultipleActiveResultSets = True " / > 6 <! -- Definindo o provedor para o Membership -- > 7 < add 8 name = " DefaultConnection " providerName = " System . Data . SqlClient " 9 connectionString = " Server =. SQLEXPRESS ; Database = k19copadomundo ; 10 User Id = sa ; Password = sa ; Trusted_Connection = False ; MultipleActiveResultSets = True " / > 11 </ connectionStrings > 188 www.k19.com.br
  • 199.
    189 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES Código XML C.3: Web.config 46 Acrescente o filtro de autenticação nos controladores Selecoes e Jogadores através do atributo Authorize. 1 [ Authorize ( Roles = " Administrador " ) ] 2 public class SelecaoController : Controller Código C# C.28: SelecaoController.cs 1 [ Authorize ( Roles = " Administrador " ) ] 2 public class JogadorController : Controller Código C# C.29: JogadorController.cs Controle de Erro Podemos configurar uma página de erro padrão para ser utilizada toda vez que um erro ocorrer. Exercícios de Fixação 47 Acrescente ao arquivo Web.config a tag customErrors para especificar a página de erro padrão. A tag customErrors fica dentro da tag system.web. 1 < system . web > 2 ... 3 < customErrors mode = " On " defaultRedirect = " ~/ Erro / Desconhecido " > 4 < error statusCode = " 404 " redirect = " ~/ Erro / PaginaNaoEncontrada " / > 5 </ customErrors > 6 ... 7 </ system . web > Código XML C.4: Web.config 48 Defina o controlador Erro e as páginas de erros padrão. As páginas de erro padrão serão criadas dentro da pasta Views numa subpasta Erro. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web ; 5 using System . Web . Mvc ; 6 7 namespace K19CopaDoMundo . Controllers 8 { 9 public class ErroController : Controller 10 { 11 // 12 // GET : / Erro / Desconhecido 13 14 public ActionResult Desconhecido () 15 { 16 return View () ; 17 } 18 www.k19.com.br 189
  • 200.
    P ROJETO 190 19 // 20 // GET : / Erro / PaginaNaoEncontrada 21 public ActionResult PaginaNaoEncontrada () 22 { 23 return View () ; 24 } 25 26 } 27 } Código C# C.30: ErroController.cs 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < meta name = " viewport " content = " width = device - width " / > 10 < title > Problema no servidor </ title > 11 </ head > 12 < body > 13 < h2 > Desculpe , tivemos problema em nosso servidor . Volte dentro de alguns instantes ← . </ h2 > 14 </ body > 15 </ html > Código CSHTML C.16: Desconhecido.cshtml 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < meta name = " viewport " content = " width = device - width " / > 10 < title > Página não encontrada </ title > 11 </ head > 12 < body > 13 < h2 > Página não encontrada </ h2 > 14 </ body > 15 </ html > Código CSHTML C.17: PaginaNaoEncontrada.cshtml Enviando email Quando um erro ocorre na nossa aplicação, podemos permitir que o usuário envie uma email para os administradores do sistema. Para enviar as mensagens, podemos utilizar o Web Exercícios de Fixação 49 Altere a tela de erro adicionando um formulário para o usuário escrever uma mensagem para os administradores da aplicação. 1 @{ 190 www.k19.com.br
  • 201.
    191 C.12. C ADASTRANDO E L ISTANDO S ELEÇÕES 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < meta name = " viewport " content = " width = device - width " / > 10 < title > Problema no servidor </ title > 11 </ head > 12 < body > 13 < h2 > Desculpe , tivemos problema em nosso servidor . Volte dentro de alguns instantes ← . </ h2 > 14 <p > Envie uma mensagem para os administradores do sistema . </ p > 15 @using ( Html . BeginForm ( " Envia " , " Email " ) ) 16 { 17 < div class = " editor - label " > 18 @Html . Label ( " Mensagem " ) 19 </ div > 20 < div class = " editor - field " > 21 @Html . TextArea ( " Mensagem " ) 22 </ div > 23 < input type = " submit " value = " Enviar " / > 24 } 25 </ body > 26 </ html > Código CSHTML C.18: Desconhecido.cshtml 50 Crie um controlador que envie as mensagens por email utilizando o helper WebMail. Observa- ção, utilize usuários, senhas e emails válidos do gmail para este exercício. 1 using System ; 2 using System . Collections . Generic ; 3 using System . Linq ; 4 using System . Web ; 5 using System . Web . Mvc ; 6 using System . Web . Helpers ; 7 8 namespace K19CopaDoMundo . Controllers 9 { 10 public class EmailController : Controller 11 { 12 13 public EmailController () 14 { 15 WebMail . SmtpServer = " smtp . gmail . com " ; 16 WebMail . EnableSsl = true ; 17 WebMail . SmtpPort = 587; 18 WebMail . From = " USUARIO@gmail . com " ; 19 WebMail . UserName = " USUARIO@gmail . com " ; 20 WebMail . Password = " SENHA " ; 21 } 22 // 23 // POST : / Email / Envia 24 [ HttpPost ] 25 public ActionResult Envia ( string mensagem ) 26 { 27 28 WebMail . Send ( " EMAIL " , " Copa do Mundo - Erro " , mensagem ) ; 29 return View () ; 30 } 31 32 } 33 } Código C# C.31: EmailController.cs www.k19.com.br 191
  • 202.
    P ROJETO 192 51 Crie uma página Envia.cshtml para mostrar ao usuário que a mensagem foi enviada com su- cesso e acrescente um link para a página inicial do site. 1 @{ 2 Layout = null ; 3 } 4 5 <! DOCTYPE html > 6 7 < html > 8 < head > 9 < title > Envia </ title > 10 </ head > 11 < body > 12 < div > 13 Mensagem enviada com sucesso . 14 </ div > 15 < div > 16 @Html . ActionLink ( " Voltar para página inicial " , " Index " , " Selecoes " ) 17 </ div > 18 </ body > 19 </ html > Código CSHTML C.19: Envia.cshtml 192 www.k19.com.br
  • 203.
    APÊNDICE R ESPOSTAS D Resposta do Complementar 2.1 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereLivro 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Título do Livro : " ) ; 13 string titulo = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Preço do Livro : " ) ; 16 string preco = System . Console . ReadLine () ; 17 18 System . Console . Write ( " Digite o Id da Editora do Livro : " ) ; 19 string editoraId = System . Console . ReadLine () ; 20 21 string textoInsereEditora = 22 @ " INSERT INTO Livros ( Titulo , Preco , EditoraId ) 23 VALUES ( ’ " + titulo + @ " ’, ’" + preco + @ " ’, " + editoraId + " ) " ; 24 25 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 26 { 27 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 28 conexao . Open () ; 29 command . ExecuteNonQuery () ; 30 } 31 } 32 } 33 } Código C# 2.9: InsereLivro.cs Resposta do Complementar 2.3 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereLivro 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 System . Console . Write ( " Digite o Título do Livro : " ) ; www.k19.com.br 193
  • 204.
    R ESPOSTAS 194 13 string titulo = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Preço do Livro : " ) ; 16 string preco = System . Console . ReadLine () ; 17 18 System . Console . Write ( " Digite o Id da Editora do Livro : " ) ; 19 string editoraId = System . Console . ReadLine () ; 20 21 string textoInsereEditora = 22 @ " INSERT INTO Livros ( Titulo , Preco , EditoraId ) VALUES (? ,? ,?) " ; 23 24 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 25 { 26 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 27 28 command . Parameters . AddWithValue ( " @Titulo " , titulo ) ; 29 command . Parameters . AddWithValue ( " @Preco " , preco ) ; 30 command . Parameters . AddWithValue ( " @EditoraId " , editoraId ) ; 31 32 conexao . Open () ; 33 command . ExecuteNonQuery () ; 34 } 35 } 36 } 37 } Código C# 2.14: InsereLivro.cs Resposta do Complementar 2.5 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaLivro 6 { 7 static void Main ( string [] args ) 8 { 9 string stringDeConexao = @ " driver ={ SQL Server }; 10 server = MARCELO - PC SQLEXPRESS ; database = livraria ; uid = sa ; pwd = sa ; " ; 11 12 using ( OdbcConnection conexao = new OdbcConnection ( stringDeConexao ) ) 13 { 14 string textoListaEditora = " SELECT * FROM Livros " ; 15 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 16 conexao . Open () ; 17 OdbcDataReader resultado = command . ExecuteReader () ; 18 19 while ( resultado . Read () ) 20 { 21 long ? id = resultado [ " Id " ] as long ?; 22 string titulo = resultado [ " Titulo " ] as string ; 23 double ? preco = resultado [ " Preco " ] as double ?; 24 long ? editoraId = resultado [ " EditoraId " ] as long ?; 25 26 System . Console . WriteLine ( " {0} : {1} - {2} - {3} n " , 27 id , titulo , preco , editoraId ) ; 28 } 29 } 30 } 31 } 32 } Código C# 2.21: ListaLivro.cs 194 www.k19.com.br
  • 205.
    195 R ESPOSTAS Resposta do Complementar 2.6 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class InsereLivro 6 { 7 static void Main ( string [] args ) 8 { 9 System . Console . Write ( " Digite o Título do Livro : " ) ; 10 string titulo = System . Console . ReadLine () ; 11 12 System . Console . Write ( " Digite o Preço do Livro : " ) ; 13 string preco = System . Console . ReadLine () ; 14 15 System . Console . Write ( " Digite o Id da Editora do Livro : " ) ; 16 string editoraId = System . Console . ReadLine () ; 17 18 string textoInsereEditora = 19 @ " INSERT INTO Livros ( Titulo , Preco , EditoraId ) VALUES (? ,? ,?) " ; 20 21 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 22 { 23 OdbcCommand command = new OdbcCommand ( textoInsereEditora , conexao ) ; 24 25 command . Parameters . AddWithValue ( " @Titulo " , titulo ) ; 26 command . Parameters . AddWithValue ( " @Preco " , preco ) ; 27 command . Parameters . AddWithValue ( " @EditoraId " , editoraId ) ; 28 29 conexao . Open () ; 30 command . ExecuteNonQuery () ; 31 } 32 } 33 } 34 } Código C# 2.26: InsereLivro.cs 1 using System . Data . Odbc ; 2 3 namespace Odbc 4 { 5 class ListaLivro 6 { 7 static void Main ( string [] args ) 8 { 9 using ( OdbcConnection conexao = ConnectionFactory . CreateConnection () ) 10 { 11 string textoListaEditora = " SELECT * FROM Livros " ; 12 OdbcCommand command = new OdbcCommand ( textoListaEditora , conexao ) ; 13 conexao . Open () ; 14 OdbcDataReader resultado = command . ExecuteReader () ; 15 16 while ( resultado . Read () ) 17 { 18 long ? id = resultado [ " Id " ] as long ?; 19 string titulo = resultado [ " Titulo " ] as string ; 20 double ? preco = resultado [ " Preco " ] as double ?; 21 long ? editoraId = resultado [ " EditoraId " ] as long ?; 22 23 System . Console . WriteLine ( " {0} : {1} - {2} - {3} n " , 24 id , titulo , preco , editoraId ) ; 25 } 26 } www.k19.com.br 195
  • 206.
    R ESPOSTAS 196 27 } 28 } 29 } Código C# 2.27: ListaLivro.cs Resposta do Complementar 5.1 1 @{ 2 ViewBag . Title = " Home " ; 3 } 4 5 @for ( int i = 0; i < 10; i ++) 6 { 7 < h2 > Olá @i </ h2 > 8 } Código CSHTML 5.10: Home.cshtml 196 www.k19.com.br