Para acompanhar as últimas tendências do desenvolvimento web, o ASP.NET vem passando por mudanças significativas em sua estrutura, como a mudança do XML para o JSON no armazenamento das configurações, o aproveitamento do ambiente Node.js para empacotamento das dependências do lado do cliente, a redução no tempo de inicialização da aplicação com a remoção das dependências, criação do pacote MVC 6 (unificado para o ASP.NET MVC, Web API e páginas Web), dentre muitos outras novidades interessantes.
Outra das melhorias é a possibilidade de utilizar task runners (como o Gulp e o Grunt), além de gerenciadores como o NPM e o Bower, que facilitam a manutenção dos pacotes utilizados na aplicação, sem a dependência do Nuget. Com toda essa estrutura modificada para atender aos requisitos do mercado atual, o ASP.NET vem se tornando cada vez mais poderoso para a construção e execução de aplicações web, tanto em ambiente Windows, quanto Linux e OSX.
Conhecendo as ferramentas
Para compreender, na prática, como esses novos recursos funcionam, desenvolveremos aqui uma aplicação ASP.NET 5 utilizando o Gulp e o Entity Framework 7, persistindo os dados no SQL Server 2014.
Grunt
O Grunt é uma ferramenta de build criada para automatizar algumas das tarefas que se repetem com certa frequência durante o desenvolvimento web, como minificação de arquivos e testes de código JavaScript. Ele foi completamente desenvolvido em JavaScript e é executado na plataforma Node.js.
No Grunt temos um arquivo central (Gruntfile.js) no qual descrevemos as tarefas que serão executadas no build da aplicação. Este arquivo é criado na raiz do projeto e sua estrutura pode ser vista na Listagem 1.
Listagem 1. Estrutura do Gruntfile.js.
01 module.exports = function(grunt) {
02 grunt.initConfig({
03 // aqui configuramos as tasks (ou tarefas)
04 });
05 // carregamento dos plugins
06 grunt.loadNpmTasks("nome-do-plugin");
07 };
Podemos reparar nessa estrutura que temos uma função JS que realiza a chamada de outras configurações. As regras para as tarefas que iremos executar são adicionadas na initConfig, onde podemos indicar quais serão os arquivos minificados, por exemplo. Ele recebe aqui um objeto JavaScript (em formato JSON) e por fim chamamos a função loadNpmTasks para registrar um plugin do Grunt, que vai trazer as funcionalidades necessárias. Para que tenhamos um melhor entendimento sobre o Grunt, vejamos um simples exemplo de sua utilização, onde configuraremos a minificação do JavaScript usando o UglifyJS, uma ferramenta que realiza a compressão de arquivos JS também sendo executada no Node.js. Registramos em seguida o plugin chamado de grunt-contrib-uglify, usando grunt.loadNpmTasks("grunt-contrib-uglify"), como podemos ver na Listagem 2.
Listagem 2. Exemplo de utilização do Grunt.
01 module.exports = function(grunt) {
02 grunt.initConfig({
03 uglify: {
04 "build/contato.js": "src/contato.js",
05 "build/main.js": "src/main.js"
06 }
07 });
08 // carrega plugins
09 grunt.loadNpmTasks("grunt-contrib-uglify");
10 };
Aqui estamos indicando que os arquivos contidos na pasta “src” devem ser minificados e o resultado deve ser salvo na pasta build, com o mesmo nome do original. Poderíamos também comprimir vários scripts em um único arquivo resultante, bastando indicar os seus nomes separados por vírgula, na forma de array.
Entity Framework 7
Outra ferramenta que utilizaremos é o Entity Framework (EF), que é um recurso de mapeamento objeto-relacional (ORM), o qual nos permite trabalhar com dados relacionais usando objetos específicos de domínio. O Entity Framework, hoje na sua versão 7 em pré-release, é desenvolvido ativamente pela equipe da Microsoft em colaboração com a comunidade de desenvolvedores de código aberto.
O Entity Framework 7 mantém a experiência de codificação limpa e produtiva com a qual já contávamos em sua versão anterior, incluindo o LINQ para consulta de dados na forma de coleções de objetos, o POCO para a definição das classes de modelo, sem dependência de namespaces adicionais, e com apoio ao Code First, metodologia de desenvolvimento em que começamos implementando as classes do modelo e a partir delas geramos o banco de dados. Além disso, o EF7 nos permite o acesso a dados através de armazenamentos relacionais e não-relacionais.
A nova versão do Entity Framework está consideravelmente mais leve, por ter sido desenvolvida a partir do zero para que tenha um melhor funcionamento nas nuvens em conjunto com o ASP.NET 5, tanto em dispositivos móveis quanto em cenários .NET tradicionais.
ASP.NET 5
O ASP.NET 5 foi também reconstruído para suportar a construção de aplicativos e serviços web modernos. Além de ser de código aberto, ele funciona tanto em ambiente local quanto na nuvem e está mais flexível para a execução dos aplicativos em um dos três tempos de execução oferecidos, que são o Microsoft .NET, o .NET Core e o Mono.
Esta nova versão da plataforma introduziu um novo pipeline modular para processamento de solicitações, seguindo o modelo proposto pela especificação OWIN. Além de possibilitar a hospedagem do IIS, tal característica também permite que aplicações Web construídas em ASP.NET sejam executadas a partir de aplicações que funcionam como processos self-host, como uma aplicação console ou um serviço do Windows.
Entre os principais avanços dessa nova versão está a possibilidade de desenvolvedores trabalharem em um mesmo projeto utilizando sistemas operacionais diferentes (Linux, OSX e Windows), e o fato de as aplicações agora estarem muito mais leves, pelo fato de não haver dependência do assembly System.Web, que por ser bastante grande, prejudicava o tempo de inicialização das aplicações e seu desempenho. Isso também facilita a utilização de outros editores de texto mais simples, como Sublime Text e o Visual Studio Code, uma opção gratuita e multiplataforma oferecida pela Microsoft.
Começando com o projeto ASP.NET
Agora começaremos o desenvolvimento da nossa aplicação, iniciando pela criação de um novo projeto do tipo ASP.NET Web Application, o qual chamaremos de EscolaDevmedia, como podemos ver na Figura 1. Em seguida, uma nova tela será apresentada, na qual selecionaremos o template Web Application dentro da categoria ASP.NET 5, como mostra a Figura 2. Após a seleção do template, precisaremos mudar a forma de autenticação do projeto, clicando na opção Change Authentication e selecionando a opção de não ter autenticação, como mostra a Figura 3. Por fim, desmarcaremos a opção de Host in the cloud, que nos permitiria hospedar a aplicação no Azure, e clicaremos em OK.
Nota: Neste artigo estamos utilizando a versão Beta5 do ASP.NET vNext. Em versões anteriores e futuras pode haver diferenças na estrutura do projeto e recursos disponíveis.
Ao finalizarmos a criação do projeto, veremos que nossa estrutura já veio pré-configurada, onde temos o Gulp definido como task runner padrão e que está sendo utilizado para a combinação e minificação de arquivos JavaScript e CSS presentes no lado front-end. A estrutura básica do nosso projeto pode ser vista na Figura 4.
Quando estamos desenvolvendo esse tipo de projeto, os arquivos CSS e JavaScript podem ser adicionados de duas formas distintas:
· Através do utilitário NuGet, que é uma ferramenta para busca e gerenciamento de pacotes nas aplicações .NET;
· Através da adição direta dos arquivos CSS e scripts JS à estrutura do projeto, utilizando o menu Add Existing Item ou arrastando o arquivo para o Solution Explorer.
Independente da forma escolhida, teríamos sempre que demandar certos esforços manuais para mantermos estes pacotes atualizados. Com a intensificação do uso de frameworks front-end, surgiram algumas soluções para simplificar a manipulação de arquivos de script, assim como as folhas de estilo. Hoje, temos incorporado ao ASP.NET 5 o suporte a algumas dessas ferramentas, como é o caso do Bower, que é um gerenciador baseado na instalação e restauração de pacotes client-side. Além dele, temos o Gulp, que nos permite a automatizar tarefas que envolvem a manipulação de pacotes client-side por meio de instruções baseadas em JavaScript, e o NPM (Node Package Manager), que é o ambiente onde as outras ferramentas são executadas. Hoje o ASP.NET 5 utiliza as funcionalidades oferecidas pelo Gulp para a automação de tarefas por padrão.
O template que selecionamos já possui os recursos mínimos para a criação da nossa aplicação, como é o caso do Bootstrap e Identity. No momento da criação do projeto, ele utiliza o Bower para adicionar o jQuery e o Bootstrap, além de incluir também o arquivo gulpfile.js, onde constam algumas tarefas já configuradas, e o arquivo package.json que já traz as tarefas do NPM requeridas tanto pelo Grunt como pelo Gulp.
Utilizando o Grunt e o Bower
Dando prosseguimento com as configurações do projeto, adicionaremos ao arquivo bower.json uma nova entrada, que será para o pacote Angular.js. Após a adição do Angular, teremos a nossa lista de dependências configurada como mostra a Listagem 3.
Listagem 3. Apresentação das dependências no bower.json.
01 "dependencies": {
02 "bootstrap": "3.0.0",
03 "jquery": "1.10.2",
04 "jquery-validation": "1.11.1",
05 "jquery-validation-unobtrusive": "3.2.2",
06 "hammer.js": "2.0.4",
07 "bootstrap-touch-carousel": "0.8.0",
08 "angular": "1.3.15",
09 "angular-route": "1.3.15"
10 }
Em seguida, adicionaremos novas tarefas ao Grunt para realização da cópia dos arquivos estáticos para os locais de destino. Estes pacotes serão adicionados no arquivo package.json, dentro da seção “Dependencies”, como podemos ver na Listagem 4.
Listagem 4. Adição de novas tarefas no Grunt.
01 "Dependencies": {
02 "grunt": "0.4.5",
03 "grunt-bower-task": "0.4.0",
04 "grunt-bowercopy": "^1.2.0",
05 "grunt-contrib-copy": "^0.8.0",
06 "grunt-contrib-concat": "^0.5.1",
07 "grunt-html2js": "^0.3.1"
08 }
Feito isso, adicionaremos uma nova pasta ao projeto, a qual chamaremos de App. Em seguida, criaremos dois novos diretórios, os quais chamaremos de Scripts e Templates, para adicionarmos nossos arquivos JavaScript e HTML, respectivamente.
Como podemos ver na Figura 4, temos um diretório chamado wwwroot, que é utilizado pelo projeto para armazenar os arquivos estáticos que deverão ser implementados e onde todas as bibliotecas e scripts que são necessários às aplicações deverão estar presentes. Esta, no entanto, é a pasta de destino para as tarefas do Grunt, com isso, abriremos o arquivo gruntfile.js e realizaremos algumas alterações, deixando o arquivo configurado de acordo com a Listagem 5.
Listagem 5. Alterações no arquivo gruntfile.js.
01 module.exports = function (grunt) {
02 grunt.initConfig({
03 bowercopy: {
04 options: {
05 runBower: true,
06 destPrefix: "wwwroot/lib"
07 },
08 libs: {
09 files: {
10 "angular": "angular",
11 "angular-route": "angular-route",
12 "jquery": "jquery",
13 "bootstrap": "bootstrap/dist/css"
14 }
15 }
16 },
17 copy: {
18 main: {
19 expand: true,
20 flatten: true,
21 filter: "isFile",
22 src: ["App/Scripts/*.js"],
23 dest: "wwwroot/dist/"
24 }
25 },
26 concat: {
27 options: {
28 separator: ";"
29 },
30 dist: {
31 src: ["wwwroot/dist/*.js"],
32 dest: "wwwroot/dist/appCombinado.js"
33 }
34 },
35 html2js: {
36 options: {
37 module: "ModuloTesteTemplates"
38 },
39 main: {
40 src: ["App/Templates/*.html"],
41 dest: "wwwroot/dist/templates.js"
42 }
43 }
44 });
45 grunt.registerTask("default", ["bowercopy:libs", "html2js", "copy:main", "concat"]);
Com os comandos utilizados até este momento, temos registradas as tarefas que irão instalar os pacotes do Bower no diretório wwwroot/lib. Na Listagem 6 temos o carregamento dos plug-ins necessários ao Grunt para o seu devido funcionamento.
Listagem 6. Carregamento dos plugins do Grunt.
01 grunt.loadNpmTasks("grunt-bower-task");
02 grunt.loadNpmTasks("grunt-bowercopy");
03 grunt.loadNpmTasks("grunt-contrib-copy");
04 grunt.loadNpmTasks("grunt-contrib-concat");
05 grunt.loadNpmTasks("grunt-html2js");
Neste arquivo, podemos ver que existem algumas tarefas configuradas, que são a bowercopy, copy, html2js e o concat. O bowercopy é definido para instalar as bibliotecas de front-end e copiá-las para a pasta wwwroot; já o contrib-copy é utilizado para copiar os scripts personalizados na pasta “dist”, que se encontra dentro de wwwroot; o html2js realiza a conversão dos modelos HTML para módulos Angular.js; e o contrib-concat é utilizado para concatenar todos os arquivos JavaScript em um único arquivo. Além deles, temos o default, que é uma tarefa personalizada que executa as tarefas dentro de uma ordem sequencial.
Para que tenhamos o task runner em execução, precisaremos abrir o Task Runner Explorer, clicando em View e buscando pela opção de menu Task Runner, presente em Other Windows. Com isso, o widget irá ler o arquivo gruntfile e listar todas as tarefas para executá-las. Nesse processo, podemos definir a opção de executar as tarefas do Grunt para cada compilação, para isso, basta clicarmos com o botão direito sobre a tarefa e, em seguida, escolher a opção desejada.
Trabalhando com o Gulp
O Gulp, nada mais é que um automatizador de tarefas, semelhante ao Grunt, com o diferencial de ser mais rápido, além de ter uma escrita muito mais simples. Quando falamos de automatizador de tarefas, estamos tratando de ferramentas que buscam agilizar o processo de desenvolvimento com base em ações repetitivas. Por exemplo, podemos minimizar os arquivos JavaScript e CSS, podemos otimizar imagens, dentre outras opções. Hoje, os automatizadores mais conhecidos são o Grunt e o Gulp, os quais compartilham de algumas características básicas, como é o caso de ambos utilizarem o Node.js e serem projetos Open Source.
O Gulp foi criado em meados de 2013 por Eric Schoffstall, que objetivava a simplificação e agilidade dos processos utilizando streams, onde a informação passa a ser transmitida através das tarefas utilizando o método de pipeline, reduzindo assim o número de operações I/O (entrada e saída). Este é atualmente o task runner padrão do Visual Studio 2015.
Ao visualizarmos a estrutura criada para o projeto, presente no Solution Explorer, vemos os pacotes pré-instalados, como é o caso do jQuery, Hammer, Bootstrap, dentre outros. Além disso, ao expandirmos o item Dependencies, podemos ver os diretórios Bower e NPM, onde neste último, temos o pacote Gulp instalado. Como utilizaremos o Gulp por padrão para gerenciarmos os scripts de nossa aplicação, vejamos como as configurações deverão ficar no nosso arquivo gulpfile.js, de acordo com a Listagem 7.
Listagem 7. Configuração do gulpfile.js.
01 /// <binding Clean="clean" />
02 var gulp = require("gulp"),
03 rimraf = require("rimraf"),
04 concat = require("gulp-concat"),
05 cssmin = require("gulp-cssmin"),
06 uglify = require("gulp-uglify"),
07 project = require("./project.json");
08 var paths = {
09 webroot: "./" + project.webroot + "/"
10 };
11 paths.js = paths.webroot + "js/**/*.js";
12 paths.minJs = paths.webroot + "js/**/*.min.js";
13 paths.css = paths.webroot + "css/**/*.css";
14 paths.minCss = paths.webroot + "css/**/*.min.css";
15 paths.concatJsDest = paths.webroot + "js/site.min.js";
16 paths.concatCssDest = paths.webroot + "css/site.min.css";
17 gulp.task("clean:js", function (cb) {
18 rimraf(paths.concatJsDest, cb);
19 });
20 gulp.task("clean:css", function (cb) {
21 rimraf(paths.concatCssDest, cb);
22 });
23 gulp.task("clean", ["clean:js", "clean:css"]);
24 gulp.task("min:js", function () {
25 gulp.src([paths.js, "!" + paths.minJs], { base: "." })
26 .pipe(concat(paths.concatJsDest))
27 .pipe(uglify())
28 .pipe(gulp.dest("."));
29 });
30 gulp.task("min:css", function () {
31 gulp.src([paths.css, "!" + paths.minCss])
32 .pipe(concat(paths.concatCssDest))
33 .pipe(cssmin())
34 .pipe(gulp.dest("."));
35 });
36 gulp.task("min", ["min:js", "min:css"]);
Esses scripts são executados com base em algumas tarefas utilizadas para copiar e limpar os arquivos JavaScript e CSS nas pastas que estão presentes no diretório wwwroot, onde estas tarefas ocorrem sempre que houverem alterações nos arquivos durante o desenvolvimento da aplicação. Para vermos como isso ocorre, abriremos o task runner da nossa aplicação clicando no item View do menu superior, em seguida em Other Windows e por último em Task Runner Explorer, como mostra a Figura 5.
Neste ponto, podemos tanto limpar quanto copiar as tarefas definidas no gulpfile.js, de forma que antes de executarmos qualquer uma dessas operações, podemos ver os arquivos no diretório lib, que possui uma cópia de todos os scripts e arquivos CSS por padrão. Ao executarmos a operação de Clean, ele irá limpar todos os arquivos presentes na pasta lib, o que significa que o Gulp está sendo usado para executar a tarefa de limpeza para remover os arquivos estáticos, como podemos ver na Figura 6.
Quando executamos a tarefa Copy, ela será encarregada de copiar todos os arquivos configurados no gulpfile.js para a pasta lib. Um outro arquivo importante presente no projeto é o bower.js, nele adicionamos os pacotes com os quais temos interesse em trabalhar na nossa aplicação, como podemos ver na Listagem 8.
Listagem 8. Pacotes utilizados pelo projeto.
01 {
02 "name": "ASP.NET",
03 "private": true,
04 "dependencies": {
05 "bootstrap": "3.0.0",
06 "bootstrap-touch-carousel": "0.8.0",
07 "hammer.js": "2.0.4",
08 "jquery": "2.1.4",
09 "jquery-validation": "1.11.1",
10 "jquery-validation-unobtrusive": "3.2.2"
11 }
12 }
Todos os pacotes adicionados ao projeto passam a fazer parte das dependências, que ficam abaixo do diretório wwwroot, como podemos ver na Figura 4. Veremos que na pasta do Bower estarão adicionados os pacotes apresentados nessa listagem. Caso estes pacotes não apareçam automaticamente, basta clicarmos com o botão direito sobre a pasta do Bower e dentre as opções apresentadas podemos selecionar a opção de “Restore packages” para que a restauração dos pacotes seja executada.
Criando os models da aplicação
Para o nosso projeto prático estamos interessados em criar uma escola de cursos, na qual teremos a possibilidade de ver as listas de professores e de cursos, além de podermos ainda realizar as operações de exclusão e inserção dos mesmos. Inicialmente, criaremos nossa aplicação utilizando dados estáticos. Dito isso, vamos então criar as classes que serão responsáveis pelos dados do Professor e do Curso. Para isso, criaremos um novo diretório, o qual chamaremos de Models, e em seguida criaremos nossas classes Curso e Professor, que são classes POCO com as anotações do Entity Framework e cujo código pode ser visto nas Listagens 9 e 10, respectivamente.
Listagem 9. Classe Professor.
01 using System.ComponentModel.DataAnnotations;
02 namespace EscolaDevmedia.Models
03 {
04 public class Professor
05 {
06 [Key]
07 public int IdProfessor { get; set; }
08 [Display(Name = "Nome completo")]
09 public string NomeProfessor { get; set; }
10 [Display(Name = "E-mail de contato")]
11 public string EmailProfessor { get; set; }
12 [Display(Name = "Telefone")]
13 public string TelefoneProfessor { get; set; }
14 public string Endereco { get; set; }
15 public string AreaAtuacao { get; set; }
16 [Display(Name = "Disciplina")]
17 public string MateriaLecionada { get; set; }
18 }
19 }
Listagem 10. Classe Curso
01 using System;
02 using System.Collections.Generic;
03 using System.ComponentModel.DataAnnotations;
04 using System.Linq;
05 using System.Threading.Tasks;
06 namespace EscolaDevmedia.Models
07 {
08 public class Curso
09 {
10 [Key]
11 public int CodigoCurso { get; set; }
12 public String TituloCurso { get; set; }
13 public String TipoCurso { get; set; }
14 public int CargaHoraria { get; set; }
15 public int SeAtivo { get; set; }
16 public int IdProfessor { get; set; }
17 public Professor Professor { get; set; }
18 }
19 }
Como podemos ver nessas listagens vinculamos o professor a partir de seu código a um curso. Essa vinculação é conhecida como propriedade de navegação pelo Entity Framework, de forma que ao criarmos o esquema do banco de dados com o EF, este irá inferir de forma automática que o IdProfessor deve ser uma chave estrangeira para a tabela de cursos. A anotação Key, por sua vez, indica que a coluna representa a chave primária da tabela quando for gerada.
Dando continuidade ao exemplo, realizaremos algumas modificações no arquivo Project.json, onde adicionaremos as dependências necessárias para a utilização do Entity Framework 7. Com as devidas modificações, nossas dependências deverão estar de acordo com a Listagem 11.
Listagem 11. Lista de dependências do projeto no Project.json.
01 "dependencies": {
02 "Microsoft.AspNet.Diagnostics": "1.0.0-beta5",
03 "Microsoft.AspNet.Mvc": "6.0.0-beta5",
04 "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta5",
05 "Microsoft.AspNet.Server.IIS": "1.0.0-beta5",
06 "Microsoft.AspNet.Server.WebListener": "1.0.0-beta5",
07 "Microsoft.AspNet.StaticFiles": "1.0.0-beta5",
08 "Microsoft.AspNet.Tooling.Razor": "1.0.0-beta5",
09 "Microsoft.Framework.Configuration.Json": "1.0.0-beta5",
10 "Microsoft.Framework.Logging": "1.0.0-beta5",
11 "Microsoft.Framework.Logging.Console": "1.0.0-beta5",
12 "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-beta5",
13 "EntityFramework.SqlServer": "7.0.0-beta5",
14 "EntityFramework.SQLite": "7.0.0-beta7",
15 "EntityFramework.Commands": "7.0.0-beta5"
16 }
Entre as dependências apresentadas nessa listagem, temos as do Commands, do SQL Server e do SQLite, que nos permitem utilizar um desses dois tipos de bancos de dados. No momento da publicação deste artigo, estas dependências encontram-se na versão 7.0.0-beta7, mas para nosso exemplo utilizaremos a versão beta5 e o SQL Server Local, que é uma versão mais simplificada do Express, e que por padrão já é disponibilizada com o Visual Studio. Outra modificação que precisaremos realizar é na opção “commands”, abaixo de “Dependencies”, a qual deixaremos como mostra a Listagem 12.
Listagem 12. Comandos utilizados.
01 "commands": {
02 "web": "Microsoft.AspNet.Hosting --config hosting.ini",
03 "ef": "EntityFramework.Commands"
04 }
Após a conclusão dessas modificações, devemos salvar a aplicação para que os novos pacotes sejam adicionados ao projeto. Cada um dos pacotes possui dependências para o seu devido funcionamento, mas eles se encarregam de instalá-las sem que precisemos nos preocupar com isso. Ao fim da instalação dos pacotes, teremos a nossa lista de referências dentro do DNX 4.5.1, como ilustra a Figura 7.
Após realizarmos essas modificações, criaremos uma nova classe dentro do diretório Models, que será a classe CursoDbContext, onde precisaremos usar o namespace Microsoft.Data.Entity para que possamos, a partir disso, utilizar a classe DbContext da qual nosso contexto herdará as características básicas, como podemos ver na Listagem 13.
Listagem 13. Criação da classe CursoDbContext.cs.
01 using Microsoft.Data.Entity;
02 using System;
03 using System.Collections.Generic;
04 using System.Linq;
05 using System.Threading.Tasks;
06 namespace EscolaDevmedia.Models
07 {
08 public class CursoDbContext : DbContext
09 {
10 public DbSet<Professor> Professores { get; set; }
11 public DbSet<Curso> cursos { get; set; }
12 }
13 }
Na nossa classe estamos utilizando as propriedades do tipo DbSet (linhas 10 e 11), as quais representam coleções de entidades que serão transformadas em tabelas da nossa base de dados. Precisaremos agora adicionar alguns registros de exemplo para realização dos nossos testes, para isso, criaremos uma nova classe, a qual chamaremos de DadosExemplo, como podemos ver na Listagem 14.
Listagem 14. Criação da classe DadosExemplo.cs.
01 using System;
02 using System.Linq;
03 using Microsoft.Data.Entity;
04 using Microsoft.Framework.DependencyInjection;
05 namespace EscolaDevmedia.Models
06 {
07 public static class DadosExemplo
08 {
09 public static void Initialize(IServiceProvider serviceProvider)
10 {
11 var context = serviceProvider.GetService<CursoDbContext>();
12 if (context.Database.AsRelational().Exists())
13 {
14 if (!context.Cursos.Any())
15 {
16 var edson = context.Professores.Add( new Professor {
NomeProfessor = "Edson Dionisio",
EmailProfessor = "edson.dionisio@gmail.com",
TelefoneProfessor = "8199702812",
Endereco = "Rua Santo Antônio", AreaAtuacao = "Sistemas de Informação",
MateriaLecionada = "Lógica de programação" }).Entity;
17 var marilia = context.Professores.Add( new Professor {
NomeProfessor = "Marília Késsia",
EmailProfessor = "mkessia.dionisio@gmail.com",
TelefoneProfessor = "8199702822",
Endereco = "Rua Santo Antônio", AreaAtuacao = "Analista de Sistemas",
MateriaLecionada = "Desenvolvimento de sistemas" }).Entity;
18 var caroline = context.Professores.Add(new Professor {
NomeProfessor = "Caroline Dionisio",
EmailProfessor = "carol.dionisio@gmail.com",
TelefoneProfessor = "8199702451",
Endereco = "Rua Major Lins", AreaAtuacao = "Desenvolvimento Mobile",
MateriaLecionada = "Introdução a Android" }).Entity;
19 var jose = context.Professores.Add(new Professor {
NomeProfessor = "José da Silva",
EmailProfessor = "jose@gmail.com", TelefoneProfessor = "8199999999",
Endereco = "Rua dos testes", AreaAtuacao = "Banco de dados",
MateriaLecionada = "Introdução ao SQL Server" }).Entity;
20 context.Cursos.AddRange(new Curso(){
TituloCurso = "Aprendendo o básico do SQL Server 2014",
CargaHoraria = 80, Professor = edson,
TipoCurso = "Virtual", SeAtivo = "Sim"},
21 new Curso(){TituloCurso = "TDD - Do básico ao avançado",
CargaHoraria = 60, Professor = edson, TipoCurso = "Presencial",
SeAtivo = "Sim"},
22 new Curso(){TituloCurso = "C# Avançado", CargaHoraria = 120,
Professor = marilia, TipoCurso = "Virtual", SeAtivo = "Sim"},
23 new Curso(){TituloCurso = "Novidades do Entity Framework 7",
CargaHoraria = 48, Professor = caroline,TipoCurso = "Presencial",
SeAtivo = "Não"}
24 new Curso(){TituloCurso = "Introdução a lógica de programação",
CargaHoraria = 96, Professor = jose, TipoCurso = "Presencial",
SeAtivo = "Não"}
25 );
26 context.SaveChanges();
27 }
28 }
29 }
30 }
31 }
Ao finalizarmos nossa classe exemplo, é chegada a hora de configurarmos o projeto com o banco de dados, adicionando a string de conexão ao arquivo config.json, como consta na Listagem 15.
Listagem 15. Adicionando a string de conexão do banco de dados.
01 {
02 "AppSettings": {
03 "SiteTitle": "Cursos Devmedia"
04 },
05 "Data": {
06 "ConnectionString": “Server=(localdb)\\MSSQLLocalDB;
Database=CursosDevmedia;
Trusted_Connection=True;MultipleActiveResultSets=true"
07 }
08 }
Na configuração realizada na linha 6 temos a utilização de uma versão simplificada do SQL Server Express para o desenvolvimento das aplicações, o LocalBD. Nosso próximo passo será configurar o serviço, e para isso abriremos o arquivo Startup.cs, e no método ConfigureService, adicionaremos o código que pode ser visto na Listagem 16.
Listagem 16. Configurando nosso serviço de banco de dados em Startup.cs.
01 using EscolaDevmedia.Models;
02 using Microsoft.Data.Entity;
03 services.AddEntityFramework()
04 .AddSqlServer()
05 .AddDbContext<CursoDbContext>(options =>
06 {
07 options.UseSqlServer(Configuration.Get("Data:ConnectionString"));
08 });
Podemos ver na linha 7 que chamamos o Configuration.Get, responsável por capturar a string de configuração do banco de dados. Essa configuração, que realizamos no config.json, é utilizada no momento em que temos nosso projeto em desenvolvimento, o que não ocorre quando o mesmo se encontra em produção. Ao final do método Configure(), precisaremos adicionar a chamada para a nossa classe exemplo, de forma a inicializarmos a nossa aplicação com estes dados. A instrução que devemos adicionar será a seguinte:
DadosExemplo.Initialize(app.ApplicationServices);
Com todas as configurações realizadas, daremos um build no projeto para ver se nada deu errado, o que podemos fazer utilizando as teclas de atalho Ctrl + Shift + B, ou através do menu Build, presente na barra de tarefas do Visual Studio.
Criando a base de dados
Até o momento, tudo o que fizemos foi configurar nossa aplicação e em nenhum momento abrimos o SQL Server para criarmos a nossa base de dados, pois este serviço foi deixado para o Entity Framework 7. Ao trabalharmos o novo EF, temos uma nova gama de desafios, como é o caso da utilização do comando Migrations, que para poder aproveitar seu potencial, aprenderemos a utilizar alguns recursos dos comandos DNX.
Entre as novidades do EF7, temos a maior granularidade de suas funcionalidades, distribuídas em pacotes NuGet. O recurso Migrations está contido no pacote EntityFramework.Commands, que para utilizá-lo, basta adicionar como uma dependência no nosso arquivo Project.json. Adicionarmos então este comando com o codinome ef em commands, por exemplo, como podemos ver na Listagem 17.
Listagem 17. Adição de instruções para utilização do Migrations.
01 // Adição da dependência em Dependencies
02 "EntityFramework.Commands": "7.0.0-beta5"
03 // Adição do comando em commands
04 "ef" : "EntityFramework.Commands"
Para que possamos utilizar o Migrations, precisamos abrir o prompt de comandos a partir do diretório da nossa aplicação. Para isso, clicaremos com o botão direito do mouse sobre o projeto EscolaDevmedia e em seguida devemos clicar na opção Open Folder in File Explorer, como mostra a Figura 8. Com a abertura do diretório que contém o projeto, desceremos um nível, até o diretório src, e com a tecla Shift pressionada, clicaremos com o botão direito do mouse sobre o diretório EscolaDevmedia como podemos ver na Figura 9. Dentre as opções apresentadas, devemos selecionar a opção Abrir janela de comando aqui, como mostra a Figura 10, que abrirá o prompt de comando contendo o caminho para a nossa aplicação, como podemos ver na Figura 11.
Com o nosso prompt de comandos aberto, precisaremos informar a ele qual a versão do .NET que estaremos utilizando, para isso, aplicaremos o seguinte comando:
dnvm use 1.0.0-beta5
O dnvm, ou gerenciador de versão .NET (.NET Version Manager), é um conjunto de utilitários de linha de comando que são usados para atualização e configuração do .NET Runtime. Quando utilizamos o comando dnvm use 1.0.0-beta5, estamos instruindo o gerenciador de versão para adicionar a versão beta5 do ASP.NET 5 em tempo de execução para a variável de ambiente PATH. Caso quiséssemos ver as versões disponíveis para o dnvm, bastaria utilizarmos a instrução dnvm list, que nos traria uma lista contendo todas as possibilidades. Agora que temos a definição da versão a ser utilizada, podemos agora utilizar o pacote dnx, com a utilização da seguinte instrução:
dnx . ef
Na Figura 12 vemos mais algumas opções de comandos auxiliares que podem ser usados junto ao ef, dentre as quais vale destacar o comando help que pode ser usado para obter maiores informações sobre a sintaxe e comandos disponíveis.
Dentre as opções apresentadas, utilizaremos o migration, e a ele passaremos o comando add Initial, que é responsável por adicionar o código para o projeto, permitindo ao EF7 a atualização do schema do banco de dados. Para isso, utilizaremos a seguinte instrução:
dnx . ef migration add Initial
Com essa instrução sendo passada no prompt de comandos, temos as seguintes informações, como podemos ver na Figura 13.
Como podemos observar, estamos usando o nosso contexto, e ao voltarmos para o Visual Studio veremos que um novo diretório foi adicionado ao projeto, titulado de Migrations, e que possui duas novas classes sendo disponibilizadas. O próximo comando a ser executado é o seguinte:
dnx . ef migration add apply
O comando apply cria definitivamente o banco de dados, o que criará também uma nova classe no diretório Migrations. Caso queiramos recriar a base de dados, excluindo definitivamente a base anterior, podemos utilizar o comando dnx . ef migration remove.
Criando os controladores de cursos e professores
Adicionaremos agora dois novos controllers à aplicação, chamados de CursosController e ProfessorController. Para o nosso exemplo, utilizaremos os métodos GET, DELETE, POST e GET(int id). Não utilizamos repositórios para nosso exemplo, mas esta é uma boa prática e que deve ser adotada para todos os nossos projetos. O código dessas classes pode ser visto nas Listagens 18 e 19, respectivamente, onde temos as definições das actions que responderão a cada um dos métodos HTTP citados.
Listagem 18. Criando a classe ProfessoresController.cs.
01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Threading.Tasks;
05 using Microsoft.AspNet.Mvc;
06 using EscolaDevmedia.Models;
07 namespace EscolaDevmedia.Controllers
08 {
09 [Route("api/[controller]")]
10 public class ProfessoresController : Controller
11 {
12 CursoDbContext cursoCtx;
13 public ProfessoresController(CursoDbContext contexto)
14 {
15 cursoCtx = contexto;
16 }
17 // GET: api/Professores
18 [HttpGet]
19 public IEnumerable<Professor> Get()
20 {
21 return cursoCtx.Professores.AsEnumerable();
22 }
23 // GET api/Professores/5
24 [HttpGet("{id}")]
25 public Professor Get(int id)
26 {
27 var professor = cursoCtx.Professores.Where
(p => p.ProfessorID == id).First();
28 return professor;
29 }
30 // POST api/Professores
31 [HttpPost]
32 public void Post([FromBody]Professor adicionaProfessor)
33 {
34 if (!ModelState.IsValid)
35 {
36 Context.Response.StatusCode = 400;
37 }
38 else
39 {
40 cursoCtx.Professores.Add(adicionaProfessor);
41 cursoCtx.SaveChanges();
42 }
43 }
44 // PUT api/Professores/5
45 [HttpPut("{id}")]
46 public IActionResult Put(int id, [FromBody]Professor atualizaProfessor)
47 {
48 var professorAtualizado = cursoCtx.Professores.Where
(p => p.ProfessorID ==id).First();
49 cursoCtx.Update(atualizaProfessor);
50 cursoCtx.SaveChanges();
51 return RedirectToAction("Index");
52 }
53 // DELETE api/values/5
54 [HttpDelete("{id}")]
55 public bool Delete(int id)
56 {
57 var professorExcluido = cursoCtx.Professores.Where
(p => p.ProfessorID == id).FirstOrDefault();
58 cursoCtx.Professores.Remove(professorExcluido);
59 if(cursoCtx.SaveChanges() > 0)
60 {
61 return true;
62 }
63 return false;
64 }
65 }
66 }
Listagem 19. Criação da classe CursosController.cs.
01 using EscolaDevmedia.Models;
02 namespace EscolaDevmedia.Controllers
03 {
04 [Route("api/[controller]")]
05 public class CursosController : Controller
06 {
07 CursoDbContext cursoCtx;
08 public CursosController(CursoDbContext contexto)
09 {
10 cursoCtx = contexto;
11 }
12 // GET: api/Cursos
13 [HttpGet]
14 public IEnumerable<Curso> Get()
15 {
16 return cursoCtx. Cursos.AsEnumerable();
17 }
18 // GET api/Cursos/5
19 [HttpGet("{id}")]
20 public Curso Get(int id)
21 {
22 var curso = cursoCtx. Cursos.Where(p => p. CursoID == id).First();
23 return curso;
24 }
25 // POST api/Professores
26 [HttpPost]
27 public void Post([FromBody] Curso adicionaCurso)
28 {
29 if (!ModelState.IsValid)
30 {
31 Context.Response.StatusCode = 400;
32 }
33 else
34 {
35 cursoCtx.Cursos.Add(adicionaCurso);
36 cursoCtx.SaveChanges();
37 }
38 }
39 // DELETE api/values/5
40 [HttpDelete("{id}")]
41 public bool Delete(int id)
42 {
43 var cursoExcluido = cursoCtx.Cursos.Where
(p => p.CursoID == id.FirstOrDefault();
44 cursoCtx.Cursos.Remove(cursoExcluido);
45 if(cursoCtx.SaveChanges() > 0)
46 {
47 return true;
48 }
49 return false;
50 }
51 }
52 }
Nessas classes instanciamos um objeto do tipo CursoDbContext no construtor do controlador, com o intuito de poder utilizá-lo para realizar nossas consultas à base de dados com o Entity Framework usando o objeto cursoCtx. Inicialmente, no método Index(), realizamos a consulta e com isso obtemos todos os professores inseridos no banco de dados. Em seguida, criamos o método POST, onde verificamos se ele possui informações para serem salvas na base, onde, com a existência dos dados, estes serão armazenados no banco de dados. Da mesma forma, ocorre com o método de PUT, com a diferença que passamos o código do professor como parâmetro e realizamos uma consulta para verificar se o mesmo já existe no banco. Por último, temos o método DELETE, o qual é utilizado para realizar a exclusão dos registros. Como podemos perceber nos métodos de POST e PUT, utilizamos o atributo [FromBody] no argumento, o qual informa que o objeto deve ser serializado a partir do corpo da mensagem.
Após a criação da nossa classe, executaremos o projeto e utilizaremos o Fiddler para que possamos ver os resultados das operações de inserção e listagem dos itens na base de dados, como podemos nas Figuras 14 e 15.
Nessa figura vemos o resultado da requisição com o método GET, que nos retorna uma lista de objetos no formato JSON. Note que o endereço utilizado foi aquele que definimos na linha 8 da Listagem 18, com o padrão /api/<nome do controller>.
Já no método POST temos um parâmetro sendo passado também no formato JSON, e que em tempo de execução será convertido para um objeto do tipo Professor. O mesmo ocorre para o cadastro de cursos, que segue a mesma lógica de funcionamento.
O ASP.NET 5 ainda se encontra em versão Beta, com previsão de lançamento da versão final em 2016, e com novos recursos sendo inseridos a cada novo release disponibilizado. O Entity Framework 7 também está em pré-release, o que nos leva a crer que novas funcionalidades ainda podem ser inseridas até a versão final. Conhecer essas novas versões desde já, porém, é importante para que nos mantenhamos informados sobre as tendências atuais do mercado e planejar com antecedência as migrações e novos projetos, que podem aproveitar dos novos recursos.
ASP.NET 5
http://www.asp.net/vnext
Fiddler
http://www.telerik.com/fiddler
Entity Framework 7
https://github.com/aspnet/EntityFramework/wiki/What-is-EF7-all-about
Links Úteis
- O que é Java?:
Neste curso você aprenderá o que é o Java, tecnologia para desenvolvimento de software mais utilizada pelos programadores em todo o mundo. - Python: Trabalhando com variáveis:
Nesta documentação você aprenderá a trabalhar com variáveis e constantes na linguagem Python, compreendendo como devem ser feitas as declarações e atribuições de valores.
Saiba mais sobre ASP.NET MVC ;)
- O que é ASP.NET MVC?:
Neste curso conheceremos o ASP.NET MVC, um framework para desenvolvimento de aplicações web que funciona com base no .NET Framework e nos permite criar projetos robustos utilizando uma das principais linguagens da atualidade. - Curso de ASP.NET MVC:
Com o ASP.NET MVC é possível criar diversos tipos de aplicações, com suporte aos principais requisitos desse tipo de projeto.