Por que eu devo ler este artigo:Neste artigo veremos algumas das novidades disponíveis na nova versão do .NET Framework, a 4.6, que sofreu algumas mudanças que irão impactar bastante no desenvolvimento de nossas aplicações, principalmente aplicações que utilizam Web Forms, assunto principal de nosso artigo.

O conhecimento das novidades da plataforma é fundamental para que se possa manter e atualizar projetos existente, permitindo que eles façam uso dos novos recursos e tenham seu funcionamento otimizado.

Recentemente a Microsoft apresentou a versão mais recente do Visual Studio, o Visual Studio 2015, junto com o .NET Framework 4.6, framework, presente desde 2014. Nosso intuito neste artigo é conhecermos algumas das melhorias presentes na nova versão do .NET.

Para darmos continuidade ao nosso artigo, é necessário que tenhamos o Visual Studio 2015 instalado, que podemos obter no endereço disponível na seção Links no fim do artigo.

Novidades do .NET Framework

De forma geral, sabemos que o .NET Framework é uma plataforma de desenvolvimento para construção de aplicativos Windows, Windows Phone, Microsoft Azure, Web e muito mais. Ele consiste basicamente na Common Language Runtime (CLR) e na biblioteca de classes .NET, que inclui tanto classes, quanto interfaces e tipos de valores que suportam uma ampla gama de tecnologias.

Até o momento da publicação deste artigo, a versão mais estável disponível é a 4.5.2, tendo dessa forma a versão 4.6 como uma prévia do que está por vir. Dentre as principais mudanças ocorridas, veremos três pontos chaves que são a liberação de pacotes open source do .NET Framework, melhorias de rastreamento de eventos e o suporte a códigos de páginas de codificação.

Suporte para codificações de página

O núcleo do .NET suporta principalmente codificações Unicode, e por padrão ele fornece suporte limitado para codificações de página de código.

A partir da nova versão podemos adicionar suporte a páginas de códigos que estão disponíveis no .NET Framework, mas sem ter suporte no núcleo do .NET, utilizando o método Encoding.RegisterProvider.

Para que possamos utilizá-lo, devemos utilizar o seguinte namespace: System.Text.CodePagesEncodingProvider. Alguns dos encodings suportados são os seguintes:

  • ASCII (página de código 20127);
  • ISO-8859-1 (página de código 28591);
  • UTF-7 (página de código 65000);
  • UTF-8 (página de código 65001);
  • UTF-16 e UTF-16LE (código de página 1200);
  • UTF-16BE (página de código 1201);
  • UTF-32 e UTF-32LE (página de código 12000);
  • UTF-32BE (página de código 12001).

Pacotes .NET Framework Open Source

Alguns dos pacotes .NET disponíveis como open source são as coleções imutáveis (Immutable Collections), XML e APIs SIMD, disponíveis de forma livre no GitHub, além de outras. Para acessar o código, basta buscar por corefx no GitHub.

Ao tratarmos das Immutable Collections, uma abordagem comum para utilizar objetos de estado imutável é na passagem deles livremente entre vários segmentos da aplicação. As coleções imutáveis são diferentes das coleções Read Only, pois estas não podem ser alteradas pelo provedor ou consumidor da coleção.

Melhorias para o rastreamento de eventos

Nesta nova versão, temos a possibilidade de construir um objeto EventSource diretamente e em seguida chamá-lo a partir de um dos métodos Write() para emitir um evento de auto descrição, como pode ser visto num simples exemplo a seguir:


using System.Diagnostics.Tracing
public void Write<T>(string eventName,  T data)

Neste exemplo, T é o tipo que define o evento e seus dados associados. Este tipo deve ser um tipo anônimo ou marcado com o atributo EventSourceAttribute. O argumento eventName é o nome do evento e os dados são o objeto do tipo T.

.NET Native

Esta é uma tecnologia de pré-compilação utilizada para a construção e implantação do ambiente Windows Store dos aplicativos dos aplicativos. Basicamente, a cadeia da ferramenta .NET Native converte o código fonte para código nativo em tempo de compilação (JIT), ao mesmo tempo em que é responsável pela organização do código IL (Intermediate Language) para código nativo (código específico da máquina). Isso produz aplicativos caracterizados pela inicialização e execução várias vezes mais rápidas.

A cadeia de ferramentas .NET Native começa a execução quando o compilador C# termina a compilação de uma aplicação da Windows Store. Em outras palavras, podemos dizer que a entrada para o .NET nativo é o aplicativo Windows Store construído pelo compilador C#.

Após isso, o .NET Native compila o aplicativo inteiro para uma aplicação nativa. O NGEN compila as assemblies para código nativo e os instala no cache de imagem nativa no computador local, enquanto o .NET nativo produz código nativo.

ASP.NET Web Forms

Com o lançamento do ASP.NET 5, muitas novidades estão sendo apresentadas. Com esta nova versão do framework praticamente recriada do zero, o objetivo do projeto foi alcançar vários objetivos arquitetônicos, o que implica a gama de aplicações multi-plataforma, além de também fornecer uma maior flexibilidade para os desenvolvedores.

Muitas dessas mudanças giravam em torno da dependência do System.Web.dll, que é um componente essencial para a utilização dos Web Forms e uma parte integrante do desenvolvimento web dentro do framework .NET. Com esta reformulação, esta dependência foi removida e novos tipos de funcionalidades para tecnologias mais recentes foram adicionados.

Uma das preocupações recorrentes na comunidade .NET era a possível “extinção” dos Web Forms com a chegada do ASP.NET 5, o que não ocorreu. A questão principal é que ao mesmo tempo em que esta versão estava sendo desenvolvida, grandes esforços também estavam sendo aplicados no desenvolvimento e aprimoramento do .NET Framework 4.6, que é uma outra versão do framework e que continua a avançar com os Web Forms.

Com base nesta versão, temos à nossa disponibilidade vários novos recursos para aplicações Web Forms, que têm seu maior foco na melhoria de desempenho, tempo de desenvolvimento e eficiência dentro de aplicações Forms. Entre essas melhorias podemos destacar:

  • Suporte a HTTP/2;
  • Compiladores de código DOM – Roslyn;
  • Asynchronous Model Binding;

Podemos tirar vantagens de alguns desses recursos através do download da última versão do Visual Studio 2015, que vem embalado com as últimas versões do framework: .NET 4.6 e ASP.NET 5.0.

Melhoria de desempenho com o HTTP/2

O aprimoramento das aplicações web tem exigido uma maior complexidade, e com isso o HTTP, protocolo que lida com esse intercâmbio de informações, passou por algumas mudanças para se adaptar às novas exigências. Neste momento, temos a inserção do HTTP/2 que chega com um único propósito: a melhoria do desempenho do web site.

Baseado no protocolo SPDY do Google, o HTTP/2 foi projetado para a melhoria de tempo de execução e desempenho dos sites, reduzindo a latência tanto quanto possível através de compressão de cabeçalhos, alavancando tecnologias de envio baseadas em servidores e suporte de carga paralela para os elementos da página através de uma única conexão.

O impacto positivo dessas mudanças pode ser imediatamente visto quando usamos um navegador que suporta o protocolo HTTP/2. Para que possamos tirar proveito do HTTP/2, construiremos uma aplicação simples que será capaz de suportar esse protocolo.

Com o Visual Studio 2015 devidamente instalado, abriremos ele e em seguida criaremos um novo projeto de Web Forms. Para isso, selecionaremos a opção ASP.NET Web Application e atribuiremos um nome para ela (neste exemplo foi definido como testeHttp2), como mostra a Figura 1. É necessário que tenhamos o framework .NET 4.6 selecionado para que tenhamos todos os recursos necessários para nosso exemplo.

Criação do projeto
Figura 1. Criação do projeto

Na próxima tela que será apresentada, devemos selecionar o template Web Forms, ou de forma opcional, podemos marcar a opção Empty, neste caso tendo a certeza de marcar o checkbox Web Forms para adicionar o core e as pastas necessárias para o projeto. Aqui deixaremos a opção Web Forms selecionada, como mostra a Figura 2.

Selecionando o template Web Forms
Figura 2. Selecionando o template Web Forms

Com nosso projeto criado, o próximo passo será construirmos uma página Web Forms básica que consistirá de uma coleção de imagens. Não teremos aqui um conteúdo real sendo apresentado, mas isso não influenciará no nosso propósito, que é apenas analisar as diferenças na requisição dos pedidos pelo HTTP e pelo HTTP/2.

Adicionaremos agora um novo Web Form clicando com o botão direito sobre o nosso projeto, em seguida selecionando a opção “Add” e por último, Web Form, como mostra a Figura 3, dando a ele o nome de testes.

Adicionando uma nova web form
Figura 3. Adicionando uma nova web form

Em seguida, adicionaremos o nosso código referente ao nosso formulário de testes, como podemos ver na Listagem 1.

Listagem 1. Exemplo de formulário web forms

<%@ Page Language="C#" AutoEventWireup="true" 
CodeBehind="testes.aspx.cs" Inherits="testeHttp2.testes" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title>Teste de HTTP/2 com Asp.Net 4.6</title>
</head>
<body>
   <form id="form1" runat="server">
   <div>
       <img src="imagens/imagem1.jpg"/> <br/>
       <img src="imagens/imagem2.jpg"/> <br/>
       <img src="imagens/imagem3.jpg"/> <br/>
       <img src="imagens/imagem4.jpg"/> <br/>
       <img src="imagens/imagem5.jpg"/> <br/>
       <img src="imagens/imagem6.jpg"/> <br/>
       <img src="imagens/imagem7.jpg"/> <br/>
       <img src="imagens/imagem8.jpg"/> <br/>
   </div>
   </form>
</body>
</html>

Após isso, executaremos a aplicação para ver o que está acontecendo no browser. Neste momento, não estamos nos importando com a aparência da página, pois nosso interesse é com relação ao que acontece nos bastidores.

Ao abrirmos a página, veremos nosso grupo de imagens sendo apresentado. Criamos esta pequena galeria para que possamos ter uma percepção maior com relação ao tempo de resposta pelo navegador.

Para isso, inspecionaremos o elemento no browser, seguiremos para a guia de Redes e atualizaremos a página para ver o que acontece.

Veremos com isso que o pedido será requisitado com todo o HTML e posteriormente as requisições de imagem começam a aparecer, como podemos ver na Figura 4.

Requisições feitas via HTTP
Figura 4. Requisições feitas via HTTP

Ao examinarmos as requisições HTTP apresentadas na figura, podemos observar que o tempo total de abertura do formulário e das imagens foi em torno de 7s (7002 ms), onde cada uma das imagens tem um tempo variado para a sua apresentação.

Hoje, a maioria dos navegadores lida com um pequeno número de conexões simultâneas apresentadas para um único domínio em um determinado momento, o que pode ser bom para a maioria dos cenários.

No entanto, para uma página que contém um grande número de elementos, os outros elementos terão de esperar por uma conexão disponível antes de serem carregados. O exemplo acima demonstra isso durante o carregamento das imagens.

Muitas vezes, esses atrasos são considerados pelo usuário final como sendo uma lentidão por parte do sistema, o que na realidade é uma limitação do navegador e do protocolo HTTP.

Para que possamos utilizar o protocolo HTTP/2, precisamos habilitá-lo, pois este ainda se encontra em fase de testes, assim como a nova versão do Visual Studio.

Para que possamos habilitar o protocolo no navegador Google Chrome, utilizado neste exemplo, devemos primeiramente verificar a versão do browser utilizada, que deve ser acima da 40.

Em seguida, devemos acessar a URL chrome://flags/, e habilitar a opção SPDY/4. Após esta opção ter sido habilitada, devemos reiniciar o browser para que as novas configurações tenham efeito.

Como podemos perceber na Figura 5, as Ferramentas de Desenvolvimento produzem uma forma um pouco diferente para realizar as solicitações usando o HTTP/2. De acordo com a imagem, podemos ver que as solicitações foram carregadas em paralelo para que fosse eliminado o atraso por parte da perspectiva do usuário.

Requisição realizada com HTTP/2
Figura 5. Requisição realizada com HTTP/2

Outra novidade significativa diz respeito à adição do compilador de código Roslyn, cuja utilização é, de certa forma, um dos maiores avanços presentes nas mudanças ocorridas dentro dos frameworks .NET 4.6.

O Roslyn é um compilador open source para API’s ricas em análise de código. Podemos construir ferramentas de análise de código com as mesmas API’s que a Microsoft está usando para implementar Visual Studio.

A exposição a estas API’s fornece aos desenvolvedores a capacidade de gerar e otimizar ainda mais o código existente, por receber o feedback do próprio compilador e permitir o uso de recursos realmente interessantes.

É o caso, por exemplo, da compilação dinâmica, que abre todos os tipos de novas portas para o desenvolvimento de aplicações ASP.NET, como os compiladores de código DOM disponíveis.

No .NET 4.6 as aplicações Web Forms utilizarão o código do compilador de forma nativa, o que permite aos desenvolvedores tirarem mais proveito de muitos dos novos recursos de linguagem disponíveis.

Antes das aplicações Web Form 4.6, as aplicações utilizavam compiladores baseados em framework de código DOM para a realização de quaisquer operações de compilação em tempo de execução. Este compilador mais antigo não entenderia como usar qualquer um dos recursos presente nas linguagens mais recentes, como o Roslyn faz.

Criaremos agora mais um exemplo, onde veremos um formulário que utiliza alguns recursos presentes no C# 6.0, como mostra a Listagem 2.

Listagem 2. Exemplo de utilização de recursos do C# 6.0

<%@ Page Language = "C#" AutoEventWireup="true" CodeBehind="testes.aspx.cs" 
Inherits="testeHttp2.testes" %>
<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Teste de HTTP/2 com Asp.Net 4.6</title>
</head>
<body>
<form id = "form1" runat= "server" >
   <pre> utilizando interpolação de strings</pre>
   <%
        // Aqui são definidas duas variáveis de exemplo para serem 
        concatenadas usando a interpolação de strings do C# 6.0
        var firstName = "Edson";
        var lastName = "Dionisio";
        // Tentativa de usar o recurso de sequência de interpolação do 
        C# 6.0 para a formatação de strings
        var output = $"{lastName}, {firstName}";
   %>
   <!-- Escrita do resultado -->
   <%= output %>
   <pre>Exemplo 2: Filtros de exceção</pre>
   <%
       // Definir uma variável que vai ser "pega" no filtro de exceção
       var x = 42; 
       try
       {
           // Definição de um objeto nulo
           object resultado = null;
           // Disparando uma exceção de null reference
           var stringifiedResult = resultado.ToString();
       }
       catch(NullReferenceException ex) if (x == 42){
           // Lidando com a exceção explicitamente 
           Response.Write("A exceção foi tratada no bloco de referência 
           nula quando x foi de 42.");
       }
       catch(Exception ex)
       {
           Response.Write($"x foi {x} e não 42, por isso ele foi tratado 
           no bloco catch genérico.");
       }
   %>
</form>

Caso não estejamos utilizando o .NET 4.6 para nossa aplicação, ou mesmo não estivermos utilizando o compilador Roslyn através de uma outra rota, como é o caso da utilização do pacote NuGet, nossa aplicação poderá ser criada, mas em seguida ela irá lançar um erro de compilação quando os recursos de linguagem mais recentes forem carregados.

Mas se ao invés disso usarmos nossa aplicação com o .NET 4.6, podemos ver que ele compila corretamente a aplicação, no entanto, quando ela atinge as características mais recentes do C# 6.0, ele usa o Roslyn para compilar e lidar com este novo código em tempo de compilação, processando o código conforme esperado.

Enquanto um recurso como a interpolação de strings pode não ser uma mudança significativa, este é apenas um dos muitos recursos novos que foram introduzidos no C# 6.0.

Suporte ao Asynchronous Model Binding

O .NET 4.5 introduziu o asynchronous baseado em tarefas populares, utilizando métodos que têm permitido aos desenvolvedores criarem funcionalidades assíncronas facilmente dentro das aplicações.

Esta mesma versão do framework também deu origem ao modelo de binding, bem como o suporte a uma variedade de controles específicos de dados, como loops, ListViews, dentre outros que possam ser preenchidos por métodos reais, em oposição a fontes de dados.

Com o lançamento do .NET 4.6, ambos os conceitos foram expandidos com a introdução do suporte a modelos de ligação assíncronos (Asynchronous Model Binding). Este tem por finalidade adicionar suporte aos vários métodos que regem nossas fontes de dados de forma assíncrona e que aguardam as chamadas, criando assim uma aplicação assíncrona e mais eficiente.

Vinculação de dados a Entity Framework

Embora possamos encontrar as conexões ADO.NET em uma ampla gama de aplicações, temos também que o surgimento dos ORMs nos últimos anos tem abordado de forma mais fácil a interação com dados, sem a necessidade de utilizarmos um assistente de SQL.

O modelo assíncrono de ligação pode ser um recurso útil para os desenvolvedores que trabalham com Entity Framework, bem como aqueles que simplesmente querem fazer as suas transações de dados um pouco mais eficientes.

Começar a usar a ligação de modelo assíncrono é muito fácil e não exige muito trabalho de nossa parte, tanto para aplicações novas, como para aplicações já existentes. Podemos demonstrar essa funcionalidade através da construção de uma única página ASPX que se conecta a um contexto de dados imaginário, e controla todas as operações de CRUD necessárias que comumente iremos encontrar dentro de uma aplicação de forma assíncrona. Essas operações podem ser vistas na Listagem 3.

Listagem 3. Operações de CRUD

<%@ Page Language="C#" AutoEventWireup="true" Async="true" 
CodeBehind="Default.aspx.cs" Inherits="TesteAsynchronousModel.Default" %>
<title>Suporte a Asynchronous Model Binding</title>
<form id=""form1"" runat=""server"">
 <asp:gridview id="WidgetTestesGrid" runat="server" datakeynames="Codigo" 
 itemtype="TesteAsynchronousModel.Models.WidgetTeste" 
     selectmethod="Selecao" updatemethod="Atualiza" deletemethod="Delete">
   <columns>
     <asp:dynamicfield datafield=""Codigo"">
     <asp:dynamicfield datafield=""Nome"">
     <asp:templatefield headertext="Testes"="">
       <itemtemplate>
         <asp:label text=""<%#" item.testes.count(p=""> p.EstaAtivo) 
         %>" runat="server"></asp:label>
       </itemtemplate>
     </asp:templatefield>
     </asp:dynamicfield></asp:dynamicfield>
   </columns>
 </asp:gridview>
</form>

Existem algumas informações importantes a serem apresentadas com relação à GridView neste momento, especialmente caso não estejamos familiarizados com o modelo de empacotamento (wrapping) introduzido com o .NET 4.5, onde temos que:

  • ItemType é uma classe rígida que corresponde aos objetos que serão usados para preencher as linhas do grid.
  • SelectMethod, UpdateMethod e DeleteMethod irão apontar para métodos do lado do servidor definidos no código que é usado para lidar com a realização das respectivas operações.
  • ItemTemplate define uma formatação especial para uma determinada coluna do grid, permitindo agrupar vários componentes ligados às propriedades dos itens listados.

Podemos dar uma olhada nos bastidores do code-behind para ver os eventos assíncronos a que cada um desses métodos corresponde e como eles são construídos dentro de um ambiente assíncrono, conforme mostra Listagem 4.

Listagem 4. Eventos assíncronos

using TesteAsynchronousModel.Models;
using System;
using System.Data.Entity;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI.WebControls; 
namespace TesteAsynchronousModel
{
   public partial class Default : System.Web.UI.Page
   {
       protected void Page_Load(object sender, EventArgs e)
       {
       }
       public async Task<selectresult> Selecao()
       {
          // Criação do contexto de dados para acessar os dados
         using (var contextoABM = new ContextoTeste())
           {
         // Criação de consultas
             var query = contextoABM.WidgetTestes.Include("Parts");
      //Retornando um selectResult para vincular os dados para o 
      GridView (usando o asyncronous método ToListAsync() 
      exposto por Entity Framework)
               return new SelectResult(query.Count(), await 
               query.ToListAsync());
           }
       }
      public async Task Atualiza(Guid codigo, CancellationToken token)
       {
           using (var contextoABM = new ContextoTeste())
           {
          var widgetTeste = await contextoABM.WidgetTestes
          .FirstAsync(w => w.Codigo == codigo);
              // Verificando se a requisição foi encontrada
               if (widgetTeste != null)
               {
                 //podemos atualizar o WidgetTeste aqui, adicionando
                 qualquer lógica personalizada da forma que precisarmos
                   TryUpdateModel(widgetTeste);
              // neste ponto, verificamos se o modelo é válido, onde em 
              caso positivo, ele irá atualizar e salvar o WidgetTeste
                   if (ModelState.IsValid)
                   {
                       await contextoABM.SaveChangesAsync();
                   }
               }
         //Caso contrário, teremos que o widgetTeste não foi encontrado e 
         dessa forma, retornaremos um erro
             ModelState.AddModelError("", $" O WidgetTeste com código = 
             '{codigo}' não foi encontrado...");
           }
       }
       public async Task Delete(Guid codigo, CancellationToken token)
       {
           using (var contextoABM = new ContextoTeste())
          {
               // Aqui tentamos recuperar o WidgetTeste através do contextoABM
               var widgetTeste = await contextoABM.WidgetTestes
               .FirstAsync(w => w.Codigo == codigo);
               if (widgetTeste != null)
               {
                   // Removendo a requisição WidgetTeste
                   contextoABM.WidgetTestes.Remove(widgetTeste);
                   await contextoABM.SaveChangesAsync();
               }
               // caso contrário, apresentaremos um erro, caso o 
               widgetTeste não tenha sido encontrado
               ModelState.AddModelError("", $"O WidgetTeste com 
               código = '{codigo}' não foi encontrado...");
           }
       }
   }
}
</selectresult>

Após a atualização dos tipos de retorno de cada um dos métodos para os objetos Task, e indicando quais os métodos assíncronos que devem ser aguardados, tudo deve funcionar de forma satisfatória.

A adição desses recursos assíncronos, como visto nessa listagem, permitirá tirarmos proveito de forma rápida e fácil dos benefícios que o async oferece. Essas melhorias recentemente aplicadas, mostram que os Web Forms não estão morrendo. Embora possamos não ver todos os olhares sendo apontados para ele, os Web Forms continuam a melhorar.

Como forma de melhorarmos nossos conhecimentos sobre o assunto apresentado, criaremos um exemplo no qual estaremos trabalhando com o ASP.NET 4.6, onde teremos a criação de stored procedures e uma classe Helper, além de uma conexão com o banco de dados MySQL. Para prosseguirmos, devemos ter instalado o banco de dados MySQL (ver seção Links).

Além disso, precisaremos também do conector do MySQL para o .NET (disponível na seção Links). Após termos o MySQL instalado, começaremos então a criar nossa base de dados e nossa tabela para darmos seguimento ao artigo.

Nomearemos a nossa base de dados aqui como testesDevmediaAsp46. Após criarmos nosso database, criaremos nossa tabela, primeiramente selecionando o banco de dados com o seguinte comando: Use ‘NomeDatabase’. Onde NomeDatabase é o nome que atribuímos ao nosso banco de dados. Feito isso, criaremos nossa tabela com o script que pode ser visto na Listagem 5.

Listagem 5. Criando a tabela funcionário no banco de dados MySQL

use testesDevmediaAsp46;
CREATE TABLE funcionario   
(
    codigo int NOT NULL AUTO_INCREMENT,
    Nome varchar(150) NOT NULL,
    email varchar(255) NOT NULL,
    telefone varchar(14),
    profissao varchar(12) NOT NULL,
    data_entrada datetime,
    funcao varchar(255),
    PRIMARY KEY(codigo)
);

Com a nossa tabela criada, inseriremos inicialmente alguns dados de testes executando, por exemplo, o script da Listagem 6.

Listagem 6. Inserção de dados de testes

insert into funcionario(Nome, email, telefone, profissao, data_entrada, 
funcao) values('Edson Dionisio', 'edson.dionisio@gmail.com', '8197402800', '
Desenvolvedor .NET', now(), 'Desenvolvimento de sites e sistemas');

insert into funcionario(Nome, email, telefone, profissao, data_entrada, 
funcao) values('Stefan Salvatore', 'stefan.salvatore@gmail.com', '8197445800', '
Desenvolvedor Java', now(), 'Desenvolvimento de sistemas em Java');

insert into funcionario(Nome, email, telefone, profissao, data_entrada, 
funcao) values('Zé dos testes', 'ze.testes@gmail.com', '8197445800', '
Desenvolvedor Java', now(), 'Desenvolvimento de sistemas em PHP');

insert into funcionario(Nome, email, telefone, profissao, data_entrada, 
funcao) values('Keiko Urameshi', 'keiko.hurameshi@gmail.com', 
'8197765800', 'Testes', now(), 'Teste de softwares');

Agora que temos nossa base de dados criada e populada, partiremos para a criação da aplicação ASP.NET. Primeiramente criaremos um novo projeto com a opção de ASP.NET Web Application, e daremos a ele o nome de projetoASPNet46, como mostra a Figura 6. Em seguida, selecionaremos o template Web Forms, dentre os templates ASP.NET 4.6 apresentados, como mostra a Figura 7.

Criando o projeto
Figura 6. Criando o projeto
Selecionando o Template 4.6
Figura 7. Selecionando o Template 4.6

Com o nosso projeto criado, faremos uso do Nuget Package Manager para adicionarmos as referências do MySQL.Data na nossa aplicação. Para isso, clicaremos com o botão direito sobre o projeto e selecionaremos a opção NuGet packages.

Na tela seguinte buscaremos por MySQL, como mostra a Figura 8. Quando as opções forem apresentadas, devemos selecionar o MySQL.Data, de acordo com a marcação “1” na figura.

Selecionando pacote MySQL.Data a ser instalado
Figura 8. Selecionando pacote MySQL.Data a ser instalado

Para que possamos finalizar a instalação das referências, precisamos aceitar as condições apresentadas.

Com todo nosso ambiente preparado, adicionaremos uma pasta ao nosso projeto chamada de DevmediaTeste, dentro da qual criaremos mais duas pastas, chamadas de bancoMysqlHelper e bancoClass, respectivamente. Dentro de cada pasta, teremos as classes bancoHelper.cs e bancoDados.cs.

Dentro do arquivo "bancoMysqlHelper.cs", criaremos uma classe auxiliar responsável pela conexão a um banco de dados MySQL, além de todo o processamento e execução de consultas.

Nesta mesma classe, criamos uma função que será responsável por executar um stored procedure. Com a classe bancoMysqlHelper.cs aberta, começaremos então a adicionar nosso código, como mostra a Listagem 7.

Listagem 7. Criando a classe bancoMysqlHelper.cs

using MySql.Data.MySqlClient;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
namespace projetoASPNet46.DevmediaTeste.bancoMysqlHelper
{
   public class bancoMysqlHelper
   {
        public String ConnectionString = 
        ConfigurationManager
        .ConnectionStrings["testedevmediaasp46"].ToString();
        public MySqlConnection conexao;
        public bancoMysqlHelper()
        {
           Initialize();
        }
        private void Initialize()
        {
           ConnectionString = ReadConnectionString();
           conexao = new MySqlConnection(ConnectionString);
        }
        public String ReadConnectionString()
        {
                 return ConnectionString = ConfigurationManager.
                 ConnectionStrings["testedevmediaasp46"].ToString();
        }
        public bool OpenConnection()
        {
           try
           {
               conexao.Open();
               return true;
           }
           catch (MySqlException ex)
           {
           }
           return false;
        }
        public bool CloseConnection()
        {
           try
           {
               conexao.Close();
               return true;
           }
           catch (MySqlException ex)
           {
               return false;
           }
        }

Até este momento não deve haver nenhuma grande dificuldade com o código, pois a única coisa que fizemos foi definir a conexão, além dos métodos de abrir e fechar a mesma. Na próxima função a ser apresentada utilizaremos a nossa conexão e começaremos a trabalhar com os comandos, onde temos os parâmetros SQL e conexão, como podemos ver n a Listagem 8.

Listagem 8. Criando as funções de manipulação dos dados

public int ComandoSQL(String Querys)
{
   int resultado = 0;
   //Abrindo a conexão
   if (OpenConnection() == true)
    {
       //criando um comando e passando a query e a conexão do construtor.
       MySqlCommand cmd = new MySqlCommand(Querys, conexao);
       //Executando o comando
       resultado = cmd.ExecuteNonQuery();
       //close conexao  
       CloseConnection();
   }
   return resultado;
}
//Retorna os dados como Dataset
public DataSet DataSet_return(String Querys)
{
   DataSet ds = new DataSet();
   if (OpenConnection() == true)
   {
       //SQL de seleção
       MySqlCommand cmdSel = new MySqlCommand(Querys, conexao);
       MySqlDataAdapter da = new MySqlDataAdapter(cmdSel);
       da.Fill(ds);
       CloseConnection();
   }
   return ds;
}
//Seleciona os resultados e retorna um Datatable 
public DataTable DataTable_return(String Querys)
{
   DataTable dt = new DataTable();
   if (OpenConnection() == true)
   {
       MySqlCommand cmdSel = new MySqlCommand(Querys, conexao);
       MySqlDataAdapter da = new MySqlDataAdapter(cmdSel);
       da.Fill(dt);
       CloseConnection();
   }
   return dt;
}

Neste conjunto de código estamos recuperando as informações vindas de uma base de dados e em seguida passando-as para um DataTable. A nossa próxima função será referente à utilização dos stored procedures, onde teremos nossos dados e passaremos eles para um DataTable, como podemos ver na Listagem 9.

Listagem 9. Criando a função Stored procedure

public DataSet SPRetornaDados(String Nome, params 
MySqlParameter[] comandosParametro)
{
   DataSet ds = new DataSet();
   if (OpenConnection() == true)
   {
       MySqlCommand cmdSel = new MySqlCommand(Nome, conexao);
       cmdSel.CommandType = CommandType.StoredProcedure;
       //Atribui os valores fornecidos para os parâmetros
       AssignParameterValues(comandosParametro, comandosParametro);
       ParametroSql(cmdSel, comandosParametro);
       MySqlDataAdapter da = new MySqlDataAdapter(cmdSel);
       da.Fill(ds);
       CloseConnection();
   }
   return ds;
}
private static void ParametroSql(MySqlCommand comando, 
MySqlParameter[] comandosParametro)
{
    if (comando == null) throw new ArgumentNullException("comando");
    if (comandosParametro != null)
    {
        foreach (MySqlParameter paramSQL in comandosParametro)
        {
            if (paramSQL != null)
            {
                if ((paramSQL.Direction == ParameterDirection.InputOutput 
                || paramSQL.Direction == ParameterDirection.Input) &
                & (paramSQL.Value == null))
                {
                    paramSQL.Value = DBNull.Value;
                }
                comando.Parameters.Add(p);
            }
        }
    }
}
private static void AssignParameterValues(MySqlParameter[] 
comandosParametro, object[] parameterValues)
{
   if ((comandosParametro == null) || (parameterValues == null))
   {
       // Caso não tenhamos valores, não será realizada nenhuma operação
       return;
   }
   if (comandosParametro.Length != parameterValues.Length)
   {
       throw new ArgumentException("A quantidade de parâmetros não é igual.");
   }
   // Aqui percorre os SqlParameters, atribuindo os valores a partir da 
   posição correspondente no Array de valores.
   for (int i = 0, j = comandosParametro.Length; i<j; i++)
   {
       // Se o valor do array atual deriva de IDbDataParameter, em seguida, 
       atribuir sua propriedade Value
       if (parameterValues[i] is IDbDataParameter)
       {
                IDbDataParameter paramInstance = 
                (IDbDataParameter)parameterValues[i];
           if (paramInstance.Value == null)
           {
               comandosParametro[i].Value = DBNull.Value;
           }
           else
           {
               comandosParametro[i].Value = paramInstance.Value;
           }
       }
       else if (parameterValues[i] == null)
       {
           comandosParametro[i].Value = DBNull.Value;
       }
       else
       {
           comandosParametro[i].Value = parameterValues[i];
       }
   }
}

Com a nossa classe helper definida, criaremos agora a nossa classe "bancoDados.cs", onde teremos disponível as nossas regras de negócios para o nosso formulário web. Aqui passaremos todas as consultas e parâmetros para a classe business e da classe executiva que passa todos os parâmetros e as consultas ou stored procedures para a nossa classe auxiliar MySQL. Teremos nessa classe um objeto responsável pelo nosso bancoMySQLHelper e para os métodos da classe executiva, como podemos ver na Listagem 10.

Listagem 10. Código da classe bancoDados

using System.Data;
using MySql.Data.Types;

namespace projetoASPNet46.DevmediaTeste.bancoClass
{
 public class bancoDados
 {
     bancoMysqlHelper.bancoMysqlHelper bdSQL = 
     new bancoMysqlHelper.bancoMysqlHelper();
    public DataSet ListaFuncionariosSelect(String SP_NAME, 
    SortedDictionary<string, string> sd)
     {
         try
         {
             return bdSQL.SPRetornaDados(SP_Nome, PegaParametro(sd));
         }
         catch (Exception ex)
         {
             throw ex;
         }
     }
     public DataTable SelecaoDados(String query)
     {
         try
         {
             return bdSQL.DataTable_return(query);
         }
         catch (Exception ex)
         {
             throw ex;
         }
     }
     public int ComandoSQL(String Query)
     {
         return bdSQL.ComandoSQL(Query);
     }
     public static void AdicionaParams(referencia MySqlParameter[] 
     paramArray, string paramNome, object paramValor)
     {
         MySqlParameter parameter = new MySqlParameter(paramNome, paramValor);
         AdicionaParams(ref paramArray, parameter);
     }

Neste momento, temos as funções responsáveis por buscar os dados da tabela e de adicionar os parâmetros. Na Listagem 11 temos a função responsável por receber os parâmetros.

Listagem 11. Criação dos métodos AdicionaParams

public static void AdicionaParams(ref MySqlParameter[] 
paramArray, string paramNome, object paramValor, object parameterNull)
{
   MySqlParameter parameter = new MySqlParameter();
   parameter.ParamNome = paramNome;
  if (paramValor.ToString() == parameterNull.ToString())
       parameter.Value = DBNull.Value;
   else
       parameter.Value = paramValor;

   AdicionaParams(ref paramArray, parameter);
}
public static void AdicionaParams(ref MySqlParameter[] paramArray, 
string paramNome, SqlDbType tipoDB, object paramValor)
{
   MySqlParameter parameter = new MySqlParameter(paramNome, tipoDB);
   parameter.Value = paramValor;
   AdicionaParams(ref paramArray, parameter);
}

public static void AdicionaParams(ref MySqlParameter[] paramArray, 
string paramNome, SqlDbType tipoDB, ParameterDirection direction, 
object paramValor)
{
   MySqlParameter parameter = new MySqlParameter(paramNome, tipoDB);
   parameter.Value = paramValor;
   parameter.Direction = direction;
   AdicionaParams(ref paramArray, parameter);
}

Com nossas classes definidas, chegou a hora de criarmos nossa interface com o cliente, onde teremos a possibilidade de ver o nosso CRUD funcionando. Começaremos então criando o stored procedure para a realização das pesquisas a partir das informações de nome e email, o qual podemos ver na Listagem 12.

Listagem 12. Criando a stored procedure para pesquisa

DELIMITER  
CREATE DEFINER =`root`@`localhost` PROCEDURE 
`USP_SelectItemmaster`(IN P_email varchar(255), 
IN P_Nome varchar(150))
BEGIN
SELECT codigo, Nome, email, telefone, profissao, 
data_entrada, funcao
FROM funcionario
where email like CONCAT(TRIM(IFNULL(P_email'')),'%') 
and Nome like CONCAT(TRIM(IFNULL(P_Nome, '')),'%');
END

Com nosso procedure criado, implementaremos agora o botão que será responsável por realizar a pesquisa, cujo código do evento Click pode ser visto na Listagem 13. Através do botão de buscaDados, estaremos passando tanto o nome quanto o e-mail por TextBoxes como parâmetro para o stored procedure, para que em seguida seja retornado o resultado.

Listagem 13. Adicionando o código ao botão de buscaDados

protected void btnPesquisa_Click(object sender, ImageClickEventArgs e)
{
  ListaFuncionariosSelect();
}
private void ListaFuncionariosSelect()
{  
   SortedDictionary<string, string> sd = new SortedDictionary
<string, string>() { };  
   sd.Add("@P_Codigo", txtEmail.Text.Trim());  
   sd.Add("@P_Nome", txtNome.Text.Trim());  
   DataSet ds = new DataSet();  
   ds = buscaDados.ListaFuncionariosSelect("Seleção de funcionários", sd);  
   GridView1.DataSource = ds;
   GridView1.DataBind();  
}

Agora passaremos a trabalhar com a inserção dos dados na tabela, onde teremos que adicionar um novo item para a tabela de funcionários. Dessa forma, ao clicarmos no botão “Adicionar”, teremos a visualização de todos os campos que devem ser preenchidos pelo usuário e, em seguida, inseriremos no banco de dados.

Para a inserção dos dados, utilizaremos também um stored procedure, que chamaremos de StoredInsereFuncionario e pode ser visto na Listagem 14.

Listagem 14. Stored procedure para inserção de funcionários

DELIMITER // 
CREATE PROCEDURE StoredInsereFuncionario(  
IN P_Nome varchar(255),
IN P_Email varchar(255),
IN P_telefone varchar(14),
IN P_Profissao varchar(150),
IN P_data_entrada DateTime,
IN P_funcao varchar(150)
) BEGIN IF NOT EXISTS(  
SELECT 1 FROM funcionario  
WHERE Nome = P_Nome  
and Status = 'N'  
) THEN BEGIN insert into funcionario(
Nome, email, telefone, profissão, data_entrada, função, Status
)
values  
(  
P_Nome, P_email, P_telefone, P_profissao, now(), P_funcao, 'N'
);  
select  "Inserido" as "Resultado"; 
end;  
ELSE  
select  
"Existe na base de dados" as "Resultado";
ENd IF;  
END // DELIMITER;

Com este procedure, verificaremos se já temos cadastrado um funcionário na base de dados, caso exista, apresentaremos uma mensagem para o usuário informando a existência dos dados na base de dados. Já no caso de não existir, o mesmo será inserido.

Após a criação do procedure, criaremos o botão “Adicionar” que é responsável pela inserção dos dados na tabela, como mostra a Listagem 15. Neste momento, passaremos todos os parâmetros para a função insereFuncionario. Caso o item seja inserido com sucesso, apresentaremos apenas uma mensagem de que o item foi inserido.

Listagem 15. Adicionando o código do botão Adicionar

private void Adicionar() {  
   SortedDictionary <string, string> sd = new SortedDictionary 
   <string, string> () {};  
   sd.Add("@P_Nome", txtNome.Text.Trim());
   sd.Add("@P_email", txtEmail.Text.Trim());
   sd.Add("@P_telefone", txtTelefone.Text.Trim());
   sd.Add("@P_profissao", txtProfissao.Text.Trim());
   sd.Add("@data_entrada", now());
   sd.Add("@P_funcao", txtFuncao.Text.Trim());  
   DataSet ds = new DataSet();
   ds = buscaDados.ListaFuncionariosSelect("InsereDadosSP", sd);
   if (ds.Tables.Count > 0) {
       if (ds.Tables[0].Rows[0].ItemArray[0].ToString() == "Exists") {  
        Page.ClientScript.RegisterClientScriptBlock(this.GetType(), 
        "alerta", "alert('O funcionário já existe na base de dados!')", true);
           txtitemName.Focus();  
       }  
   } else {  
       clearControls();  
       ListaFuncionariosSelect();  
   }  
}

Com nossos procedures de inserção e pesquisa criados, adicionaremos agora ao nosso código mais um procedure, o qual será responsável pela atualização das informações dos funcionários, como mostra a Listagem 16.

Listagem 16. Criando a procedure de update

DELIMITER // CREATE PROCEDURE SPUpdateFuncionario (  
IN P_Codigo int,
IN P_Nome varchar(150),  
IN P_email varchar(255),
IN P_telefone varchar(14),
IN P_profissao varchar(150),
IN P_data_entrada datetime,
IN P_funcao varchar(150)
) BEGIN  
update funcionario
SET telefone = P_telefone, profissao = P_profissao, funcao = P_funcao
where Codigo = P_Codigo;
select  
"Dados atualizados" as "Resultado";
END // DELIMITER;

No botão de edição de cadastro, passaremos todos os parâmetros para a atualização do stored procedure e assim que os dados tiverem sido atualizados, atualizaremos a grid, como podemos ver na Listagem 17.

Listagem 17. Criando o código do botão de edição

private void Edita()
{  
   SortedDictionary<string, string> sd = 
   new SortedDictionary<string, string>() { };
   sd.Add("@P_Codigo", txtCodigo.Text.Trim());
   sd.Add("@P_Nome", txtNome.Text.Trim());
   sd.Add("@P_email", txtEmail.Text.Trim());
   sd.Add("@P_telefone", txtTelefone.Text.Trim());
   sd.Add("@P_Profissao", txtProfissao.Text.Trim());
   sd.Add("@P_funcao", txtFuncao.Text.Trim());
   DataSet ds = new DataSet();
   ds = buscaDados.ListaFuncionariosSelect("SPUpdateFuncionario", sd);
   ListaFuncionariosSelect();
   clearControls();
}

Por último, o usuário poderá excluir um item clicando no botão de exclusão presente no GridView. Quando o usuário clicar no botão excluir no grid, o item selecionado será excluído de acordo com o Id. Neste caso, como uma espécie de auditoria, não excluiremos realmente o registro da tabela.

Ele será desabilitado da exibição, através de uma flag que criamos na nossa tabela de funcionários, chamado de status, que por padrão definimos como "N" e para “excluirmos” o registro, atualizaremos esta informação para "Y", como mostra a Listagem 18.

Listagem 18. Exclusão do registro

private void DeleteFuncionario(String Codigo) {  
   int inserStatus = buscaDados.ComandoSQL("update funcionario 
   SET Status = 'Y' where Codigo = '" + codigo + "'");  
   ListaFuncionariosSelect();
}

Neste último momento não utilizamos stored procedures para realizar o processo de exclusão, pois neste caso a nossa intenção é apenas de deixar a informação registrada na base de dados e não mais ser apresentada no grid.

Alguns trechos de código não foram postos aqui devido à sua extensão, estando eles disponíveis no código-fonte, mas a partir dos recursos aqui explorados é possível perceber que os Web Forms não foram abandonados pela Microsoft, como muito foi especulado.

Com essa continuidade e atualização, as aplicações já desenvolvidas nessa plataforma poderão tanto serem mantidas, como também atualizadas para tirarem proveito das novidades da versão 4.6.