sexta-feira, 31 de maio de 2013

função que retorna um valor escalar no SQL Server

VB .NET – Executando uma função que retorna um valor escalar no SQL Server

Hoje vou mostrar como recuperar um valor de uma função definida pelo usuário no SQL Server que retorna um valor escalar usando ADO .NET. Para fazer isso, vamos criar a Function (Função) no SQL Server e tratá-la como se fosse um procedimento armazenado. A partir do SQL Server 2000 temos o recurso chamado Functions, que permite criar funções para auxiliar na consulta e obtenção de informação.
Uma função definida pelo usuário é uma rotina Transact-SQL ou CLR (Common Language Runtime) que aceita parâmetros, executa uma ação (como um cálculo complexo) e retorna o resultado dessa ação como um valor. O valor de retorno pode ser um valor escalar (único) ou uma tabela.
A criação de uma user function é similar à criação de uma stored procedure ou uma view. Atualmente, temos a possibilidade de criar funções dos seguintes tipos:
  • Inline Function – São usadas para parametrizar views;
  • Table-valued Function – Usada para lógicas complexas; retorna uma tabela;
  • Scalar-valued Function – Retorna apenas um parâmetro; são semelhantes as funções pré-existentes no SQL Server(Ex: getDate())
Vamos, então, criar uma Scalar-valued Function e mostrar como podemos tratar o seu retorno usando ADO .NET. Os recursos necessários usados neste artigo foram:
  • Visual Basic 2010 Express Edition;
  • SQL Server 2008 Express;
  • Banco de dados Northwind.mdf.
Abra o Visual Basic 2010 Express Edition e crie um novo projeto (File-> New Project) do tipo Windows Forms Application com o nome de SQLServer_FuncaoEscalar:

Agora vamos abrir a janela DataBase Explorer e exibir os objetos do banco de dados Northwind.mdf. Depois clique com o botão direito sobre o item Functions e selecione Add New -> Scalar-valued Function.

Será aberta uma janela com uma estrutura pronta para criarmos a nossa função:

Defina a função com o nome ValorEstoque, conforme mostra a figura a seguir:

Esta função calcula o valor do estoque para um produto multiplicando o seu preço unitário pelo sua quantidade em estoque. Agora vamos usar a instrução com um DataAdapter para preencher um DataTable com os cinco primeiros registros da tabela Products do banco de dados Northwind.mdf. Assim vamos obter o valor do estoque para cada registro usando a função criada com o valor escalar.
Na janela Solution Explorer selecione o formulário form1 e a partir da ToolBox vamos incluir os seguintes controles no formulário:
  • DataGridView – name = gdvDados
  • Button – btnExecutar
Conforme o leiaute abaixo:

No evento Click do botão Executar, vamos incluir o código abaixo:
Private Sub btnExecutar_Click(ByVal sender As System.Object, ByVal e As 
System.EventArgs) Handles btnExecutar.Click

        Try
            Dim sqlConnectString As String = "Data Source=.\SQLEXPRESS;Integrated 
security=SSPI;Initial Catalog=Northwind;"

            'Dim sql1 As String = "SELECT TOP 5 *, " + "dbo.ValorProdutoEstoque
(UnitPrice, UnitsinStock) as " + "ValorEstoque FROM Products"
            Dim sql2 As String = "SELECT ProductID, ProductName, ValorEstoque=dbo.
ValorEstoque(UnitPrice, UnitsInStock) FROM Products"

            Dim da As New SqlDataAdapter(sql2, sqlConnectString)
            Dim dt As New DataTable()
            da.Fill(dt)

            gdvDados.DataSource = dt
            formatagrid()

        Catch ex As Exception
            MessageBox.Show("Erro : " + ex.Message)
        End Try

    End Sub
Estamos chamando a função na instrução SQL:
Dim sql2 As String = "SELECT ProductID, ProductName, ValorEstoque=dbo.ValorEstoque
(UnitPrice, UnitsInStock) FROM Products"
Note que temos que informar o ower da function ao chamá-la, por isso usamos dbo.ValorEstoque(UnitPrice, UnitsInStock). Se você chamar ValorEstoque(UnitPrice, UnitsInStock) não vai funcionar.
A rotina formatagrid() tem o seguinte código e é usada para formatar o controle datagridview:
Private Sub formatagrid()
        With gdvDados
            '.AutoGenerateColumns = True
            .AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.
DisplayedCellsExceptHeaders
            .ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single
            'altera a cor das linhas alternadas no grid
            .RowsDefaultCellStyle.BackColor = Color.White
            .AlternatingRowsDefaultCellStyle.BackColor = Color.Aquamarine
            'altera o nome das colunas
            .Columns(0).HeaderText = "Código"
            .Columns(1).HeaderText = "Produto"
            .Columns(2).HeaderText = "Valor do Estoque"
            .Columns(0).Width = 50
            .Columns(1).Width = 300
            .Columns(2).Width = 200
            'esconde a coluna
            .Columns("ProductID").Visible = False
            'formata as colunas valor, vencimento e pagamento
            .Columns(2).DefaultCellStyle.Format = "c"
            'seleciona a linha inteira
            .SelectionMode = DataGridViewSelectionMode.FullRowSelect
            'não permite seleção de multiplas linhas
            .MultiSelect = False
            ' exibe nulos formatados
            .DefaultCellStyle.NullValue = " - "
            'permite que o texto maior que célula não seja truncado
            .DefaultCellStyle.WrapMode = DataGridViewTriState.True
            'define o alinhamamento 
            .Columns("ValorEstoque").DefaultCellStyle.Alignment = 
DataGridViewContentAlignment.MiddleRight
        End With
    End Sub
Executando o projeto iremos obter:

Percebeu como é fácil criar e utilizar uma função definida pelo usuário (FUNCTION) no SQL Server? Você pode criar Functions para obter informações e realizar operações.
Mas qual a vantagem das Functions sobre os Procedimentos armazenados? Uma das vantagens de funções definidas pelo usuário sobre os procedimentos armazenados, é o fato de que uma UDF pode ser usada em instruções Select, Where ou em uma declaração Case.
Elas também podem ser usadas para criar associações (Joins). Além disso, funções definidas pelo usuário são mais simples para invocar do que procedimentos armazenados de dentro de outra instrução SQL.

Inserindo e obtendo imagens do SQL Server

ASP .NET – Inserindo e obtendo imagens do SQL Server (C#)

Neste artigo, veremos como inserir e obter imagens de um banco de dados SQL Server e como exibir as imagens em um controle GridView.
Acompanhe a versão para desktop deste artigo no link: Trabalhando com imagens no SQL Server – Macoratti.net
Vamos iniciar abrindo o Visual Web Developer 2010 Express Edition e criando um novo projeto via opção File -> New Project.
Selecione a linguagem Visual C# e a opção Web.
A seguir, selecione o template ASP .NET Web Application e informe o nome Tratando_Imagens_SQLServer.


Criando o banco de dados no SQL Server

Eu poderia criar o banco de dados usando o SQL Server Management Studio (SSMS), mas vou usar o próprio ambiente do Visual Web Developer Express Edition.
Abra a janela DataBase Explorer e clique com o botão direito em Data Connections, clicando a seguir em Add Connection.


Na janela Add Connection, clique no botão Change e altere o Data Source para: Microsoft SQL Server Database File.
Obs: Esta janela poderá exibir outras opções de Data Source, mas eu escolhi trabalhar com um banco de dados anexado ao meu SQL Server Local.


Informe o nome do banco de dados; no exemplo, o nome usado foi Cadastro.mdf, e clique no botão OK.


Uma caixa de diálogo irá surgir informando que o banco de dados não existe e se você deseja criá-lo. Confirme e clique no botão Sim para criar o banco de dados.
Você verá na janela DataBase Explorer o banco de dados Cadastro.
Clique com o botão direito no item Tables e a seguir clique em Add New Table;



Crie a tabela Imagens definindo os campos:
  • id – int – chave primária e do tipo identity;
  • descrição – nvarchar(150)
  • imagem – image



Assim, temos o banco de dados Cadastro.mdf e a tabela Imagens criadas e prontas para serem usadas.

Definindo as páginas do projeto

Vamos definir a página Default.aspx como a página principal do projeto clicando com o botão direito do mouse sobre essa página e selecionando a opção Set As Start Page.
Agora vamos alterar o código da página Site.Master para exibir uma opção para acessar a página onde vamos tratar as imagens conforme o código a seguir:
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" 
Inherits="Tratando_Imagens_SQLServer.SiteMaster" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head runat="server">
    <title></title>
    <link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
    <asp:ContentPlaceHolder ID="HeadContent" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form runat="server">
    <div>
        <div>
            <div>
                <h1>
                   Macoratti .net
                </h1>
            </div>
            <div>
            </div>
            <div>
                <asp:Menu ID="NavigationMenu" runat="server" CssClass="menu" 
EnableViewState="false" IncludeStyleBlock="false" Orientation="Horizontal">
                    <Items>
                        <asp:MenuItem NavigateUrl="~/Default.aspx" Text="Home"/>
                        <asp:MenuItem NavigateUrl="~/About.aspx" Text="About"/>
                        <asp:MenuItem NavigateUrl="~/Imagens.aspx" Text="Imagens"/>
                     </Items>
                </asp:Menu>
            </div>
        </div>
        <div>
            <asp:ContentPlaceHolder ID="MainContent" runat="server"/>
        </div>
        <div>
        </div>
    </div>
    <div>

    </div>
    </form>
</body>
</html>
A seguir, vamos incluir um novo Web Form chamado Imagens.aspx em nosso projeto.
No menu Project, clique em Add New Item.
Selecione o template Web Form using Master Page, informe o nome Imagens.aspx e clique no botão Add.


Na próxima janela, selecione a master page Site.Master e clique em Ok.


Vamos incluir na página Imagens.aspx a partir da toolbox os seguintes controles:
  • TextBox – ID=txtDescricao TextMode=Multiline
  • FileUpload – ID=arquivoUploadImagem
  • GridView – ID=gdvImagens
  • Button – ID=btnUpload – Text=Enviar Arquivo
Conforme o leiaute da figura abaixo:

No controle GridView, vamos definir dois campos: Descrição e Imagem.


A seguir, vamos converter o campo Imagem em um TemplateField e definir nesse template um controle Image.
Defina a seguir as propriedades DataBindings conforme a figura abaixo para esse controle:

O código do arquivo Imagens.aspx deverá ficar da seguinte forma:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup=
"true" 
CodeBehind="Imagens.aspx.cs" Inherits="Tratando_Imagens_SQLServer.Imagens" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
    Inserindo e Recuperando imagens no SQL Server com ASP .NET
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <head> 
    <style type="text/css">
        .Gridview
        {
            font-family:Verdana;
            font-size:10pt;
            font-weight:normal;
            color:black;
            }
    </style>
    </head>
    <table>
    <tr><td> Descricao:</td>
    <td> <asp:TextBox ID="txtDescricao" runat="server" BackColor="#FFFFCC" 
Height="97px" 
            TextMode="MultiLine" Width="260px"></asp:TextBox>
    </td>
    </tr><tr><td>Imagem:</td>
    <td> <asp:FileUpload ID="arquivoUploadImagem" runat="server" Width="350px" /> 
</td>
    </tr><tr><td>
    </td>
    <td><asp:Button ID="btnUpload" runat="server" Text="Enviar Arquivo" 
            onclick="btnUpload_Click" />
    </td>
    </tr>
    </table>
    </div>
    <asp:GridView ID="gdvImagens" CssClass="Gridview" runat="server" 
AutoGenerateColumns="False"
    HeaderStyle-BackColor="#7779AF" HeaderStyle-ForeColor="white" Height="766px" 
        Width="521px">
    <Columns>
    <asp:BoundField HeaderText = "Descrição" DataField="imagename" />
    <asp:TemplateField HeaderText="Imagem">
    <ItemTemplate>
    <asp:Image ID="Image1" runat="server" 
            ImageUrl='<%# "ImageHandler.ashx?ImgID="+ Eval("id") %>' 
Height="150px" 
            Width="150px"/>
    </ItemTemplate>
    </asp:TemplateField>
    </Columns>
    <HeaderStyle BackColor="#7779AF" ForeColor="White"></HeaderStyle>
    </asp:GridView>
</asp:Content>
Observe que no botão Enviar Arquivo estamos tratando o evento Click.

Definindo a string de conexão no arquivo Web.Config

Abra o arquivo Web.Config e inclua o código abaixo na seção <configuration> para definir a string de conexão com o banco de dados Cadastro.mdf:
<?xml version="1.0"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>

  <connectionStrings>
    <add name="ConexaoSQLServer"
        connectionString="Data Source =.\SQLEXPRESS;Initial Catalog=Cadastro;
Integrated Security=SSPI;"
        providerName="System.Data.SqlClient" />
  </connectionStrings>
   .........
   ........

Definindo o código para tratamento das imagens

Vamos abrir o arquivo code-behind Imagens.aspx.cs e definir o seguinte código:
No início do arquivo, defina a utilização dos seguintes namespaces:
using System; using System.Web; using System.Web.UI; using System.Data; using System.Data.SqlClient; 
using System.Configuration;
Após a declaração do formulário, obtenha a string de conexão do arquivo web.config:
string strcon = ConfigurationManager.ConnectionStrings["ConexaoSQLServer"].ConnectionString;
No evento Load da página, inclua o código abaixo:
 protected void Page_Load(object sender, EventArgs e)
   {
            if (!IsPostBack)
            {
                VincularDados();
            }
  }
No evento Click do botão Enviar inclua o código a seguir:
protected void btnUpload_Click(object sender, EventArgs e)
        {
            //Verifica se enviou imagem ou não
            if (arquivoUploadImagem.HasFile)
            {
                //obtem o tamanho da imagem enviada
                int tamanho = arquivoUploadImagem.PostedFile.ContentLength;
                //cria um array de bytes para armazenar os dados binários da 
imagem
                byte[] imgbyte = new byte[tamanho];
                //armazena a imagem selecinada na memória
                HttpPostedFile img = arquivoUploadImagem.PostedFile;
                //define os dados binários
                img.InputStream.Read(imgbyte, 0, tamanho);
                string descricaoImagem = txtDescricao.Text;
                //abre uma conexão 
                SqlConnection connection = new SqlConnection(strcon);
                connection.Open();
                SqlCommand cmd = new SqlCommand("INSERT INTO Imagens (descricao,
imagem) VALUES (@descricao,@imagem)", connection);
                cmd.Parameters.Add("@descricao", SqlDbType.VarChar, 50).Value = 
descricaoImagem;
                cmd.Parameters.Add("@imagem", SqlDbType.Image).Value = imgbyte;
                int contador = cmd.ExecuteNonQuery();
                connection.Close();
                if (contador == 1)
                {
                    VincularDados();
                    txtDescricao.Text = string.Empty;
                    ScriptManager.RegisterStartupScript(this, this.GetType(),
 "alertmessage", "javascript:alert('" + descricaoImagem + " imagem incluida 
com sucesso')", true);
                }
            }
        }
O código acima inclui uma imagem na tabela Imagens e exibe uma mensagem via JavaScript de alerta ao usuário avisando que a imagem foi incluída.
O código da rotina VincularDados() é o seguinte:
private void VincularDados()
        {
            SqlConnection connection = new SqlConnection(strcon);
            SqlCommand command = new SqlCommand("SELECT descricao,id from 
Imagens", connection);
            SqlDataAdapter daimages = new SqlDataAdapter(command);
            DataTable dt = new DataTable();
            daimages.Fill(dt);
            gdvImagens.DataSource = dt;
            gdvImagens.DataBind();
            gdvImagens.Attributes.Add("bordercolor", "black");
        }
Nesse código, estamos recuperando as imagens da tabela Imagens e vinculando no controle GridView.
Após a conclusão do código acima, precisamos adicionar um arquivo HttpHandler ao nosso projeto para recuperar imagens do banco de dados, pois nós salvamos as imagens em formato binário, e obter as imagens no formato binário é fácil, mas a exibição não é tão simples assim, e por isso vamos usar um HttpHandler para resolver esse problema.
Aqui o HttpHandler é uma classe simples que lhe permite processar um pedido e retornar uma resposta para o navegador. De forma simples, o manipulador é responsável por atender solicitações do navegador. Ela só pode lidar com uma solicitação por vez o que lhe dá um bom desempenho.
No menu Project, clique em Add new Item e a seguir selecione o template Visual C# -> Web e Generic Handler, informando o nome ImagemHandler.ashx e clique no botão Add.

A seguir inclua o código abaixo no arquivo ImagemHandler.ashx:
Neste código, incluímos a linha para obter a string de conexão do arquivo web.config e definimos o código do evento ProcessRequest() para obter e exibir a imagem:
using System;
using System.Web;
using System.Data.SqlClient;
using System.Configuration;

namespace Tratando_Imagens_SQLServer
{
    public class ImagemHandler : IHttpHandler
    {
        string strcon = ConfigurationManager.ConnectionStrings["ConexaoSQLServer"].
ConnectionString;

        public void ProcessRequest(HttpContext context)
        {
            string imagemID = context.Request.QueryString["ImgID"];
            SqlConnection connection = new SqlConnection(strcon);
            connection.Open();
            SqlCommand command = new SqlCommand("select imagem from Imagens 
where id=" + imagemID, connection);
            SqlDataReader dr = command.ExecuteReader();
            dr.Read();
            context.Response.BinaryWrite((Byte[])dr[0]);
            connection.Close();
            context.Response.End();
        }
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}
Agora podemos executar o projeto e, ao acionar o link Imagens do menu da página, poderemos incluir e exibir as imagens no controle Gridview, conforme mostra a figura abaixo:



Após selecionar uma imagem e clicar no botão Enviar Arquivo, veremos uma mensagem indicando que imagem foi incluída com sucesso

Fazendo a integração com o PayPal

ASP .NET MVC – Fazendo a integração com o PayPal

Neste artigo, eu vou mostrar como podemos realizar a integração do componente PayPal com uma aplicação ASP .NET MVC, permitindo assim que sua aplicação aceite pagamentos via PayPal.
Obs: Você pode fazer a integração usando o PayPal na loja virtual criada com o WebMatrix no curso : WebMatrix – Criando uma Loja Virtual (Curso).
O PayPal é a maneira simples e segura de pagar suas compras na Internet, no celular, no tablet ou onde você estiver. Com PayPal, seus dados financeiros (como os números de cartão de crédito e conta bancária) não são compartilhados com os vendedores e apenas você tem acesso à sua conta do PayPal.
O Helper suporta tanto a API PayPal Button Manager, quanto a API Adaptive Payments. Utilizando a primeira, você será capaz de criar (e gerenciar) botões PayPal, como Adicionar ao Carrinho ou Comprar, que permitirá que seus clientes comprem itens únicos ou múltiplos. Com a segunda API, você pode processar transações em seu web site. Dessa forma, usando o Helper, você pode construir aplicativos na web que tratem os pagamentos a serem feitos.Para isso eu vou usar o Helper PayPal que permite integrar pagamentos PayPal dentro do seu site ASP .NET, WebMatrix ou aplicação e-commerce. Com algumas linhas de código, você vai permitir que os seus clientes façam o pagamento de suas compras com suas contas do PayPal.
Como exemplo, eu vou criar uma aplicação ASP .NET MVC 4 usando o Visual Studio Express 2012 for Web.
Abra o Visual Studio Express 2012 for Web e no menu File clique em New Project e, a seguir, selecione o template Visual C# -> Web  e selecione ASP .NET MVC 4 Web Application, informando o nome Mvc4PayPal e clicando no botão OK.
mvc4_payc
A seguir, selecione o template - Internet Application e o engine Razor e clique no botão OK:
mvc4_payf
Seria criado um projeto com uma estrutura padrão de uma aplicação ASP .NET MVC, que vamos usar para implementar o PayPal.
Antes de continuar, você deverá acessar o site https://developer.paypal.com/ e criar uma conta de desenvolvimento PayPal. Crie sua conta de teste com o cartão de crédito para testes em ambiente de desenvolvimento (você não tem que pagar nada).
Obs: Voce pode acessar o site do PayPal no Brasil e realizar os mesmos procedimentos: https://www.paypal.com/br/webapps/mpp/home
Você deverá cria a sua conta de teste onde receberá o email da conta, o nome do usuário para acesso a API; a senha da API é uma assinatura que é um hash gerado para garantir a segurança.
No meu caso, vejas informações da conta de teste abaixo (por questões óbvias ocultei as minhas informações):
mvc4_payd
Após criar a sua conta de teste e gerar a suas credenciais para autenticação, você pode prosseguir – essas informações serão usadas mais à frente.
Vamos agora incluir as referências ao Helper do PayPal em nosso projeto. Podemos fazer isso usando o gerenciador de pacotes Nuget ou o Package Manager Console.
No menu TOOLS, clique em Library Package Manager e a seguir em Package Manager Console.
mvc4_paye
A  seguir, no console, digite Install-Package PayPal.Helper e tecle ENTER. Após alguns segundos, você deverá ver as mensagens indicando que o pacote foi instalado e referenciado com êxito no seu projeto:
mvc4_pay0
Agora vamos definir a inicialização da API em nosso projeto.
Abra o arquivo Global.asax.cs e no método Application_Start inclua a linha de código indicada a seguir:
mvc4_payb
A linha de código usa as informações da minha conta, minha senha e minha assinatura. Aqui você deve incluir as suas credenciais obtidas no site do PayPal.
Após isso, vamos abrir a view Index.cshtml na pasta /Home e incluir o código abaixo no início do arquivo:
mvc4_paya
Este código criará o botão Add to Cart para o produto Meu Produto com o valor 9.99 para a minha conta de testes (aqui você deve usar a sua conta de testes obtida no PayPal).
Com essas implementações, podemos rodar a aplicação onde obteremos na página gerada pela view Index.cshtml o botão Add to Cart conforme abaixo:
mvc4_pay1
Clicando no botão Add to Cart iremos para a página de informação do produto onde podemos definir a quantidade e obter o valor a ser pago.
mvc4_pay2
Ao clicar no botão Check out with PayPal teremos a exibição do pedido a solicitação das credenciais da conta para que o pagamento seja efetuado:
mvc4_pay3
A seguir clique no botão Agree e Continue para concordar com a política do PayPal:
mvc4_pay4
Será exibida uma janela com as informações do pedido, o endereço do cliente e a solicitação do pagamento no botão Pay Now;
mvc4_pay5
A tela informando que o pagamento foi feito pode ser vista a seguir:
mvc4_pay6
Verificando a conta de testes no PayPal veremos que o valor foi debitado do saldo inicial da conta:
mvc4_pay7
Verificando os detalhes da conta de testes também vemos as informações atualizadas:
mvc4_pay8
Dessa forma mostrei como é bem simples implementar a solução de pagamentos PayPal em uma aplicação ASP .NET MVC usando o Helper PayPal.

Web Forms

ASP .NET 4.5 Web Forms – Tratamento de erros e registro de log – Parte 09

Neste artigo, vamos incluir o tratamento de erros e o registro de erros em nossa aplicação.
O tratamento de erros vai permitir que o aplicativo lide de forma mais amigável com os erros, exibindo mensagens de erros adequadas em cada situação. O registro de erros permitirá que possamos encontrar e corrigir os erros que ocorreram durante a execução da aplicação.
O que você vai aprender:
  • Como adicionar manipulação de erro global para a configuração do aplicativo;
  • Como adicionar o tratamento de erros a nível de aplicação, de página e de código;
  • Como registrar erros para posterior análise;
  • Como exibir mensagens de erro que não comprometam a segurança;
  • Como implementar módulos registro de erros e manipuladores (ELMAH) de registro de erros;
Este artigo foi integralmente baseado no artigo original: http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/aspnet-error-handling (com algumas alterações e portado para a linguagem VB .NET).

Conceitos

As aplicações ASP.NET devem ser capaz de lidar com os erros que ocorrem durante a execução de uma forma consistente. A ASP.NET usa o Common Language Runtime (CLR), que fornece uma maneira de notificar as aplicações de erros de uma maneira uniforme. Quando ocorre um erro, uma exceção é lançada. Uma exceção é qualquer erro, condição ou comportamento inesperado que um aplicativo encontra durante sua execução.
Na plataforma .NET, uma exceção é um objeto que herda da classe System.Exception. Uma exceção é lançada a partir de uma área de código onde ocorreu um problema. A exceção é passada para a pilha de chamadas, um lugar onde a aplicação fornece código para manipular a exceção. Se o aplicativo não tratar a exceção, o navegador é forçado a exibir os detalhes do erro.
Como melhor prática, lidamos com os erros ao nível do código usando blocos try/catch /finally dentro do código. Geralmente, colocamos esses blocos de modo que o usuário possa corrigir problemas no contexto em que eles ocorrem. Se os blocos de tratamento de erros estiverem muito longe de onde ocorreu o erro, torna-se mais difícil fornecer aos usuários a informação do que ele precisa para corrigir o problema.

Classe de exceção

A classe Exception é a classe base da qual as exceções herdam. A maioria dos objetos de exceção são instâncias de alguma classe derivada da classe Exception, como a classeSystemException, a classe IndexOutOfRangeException, ou a classe ArgumentNullException. A classe Exception tem propriedades, tais como a propriedade StackTrace, a propriedadeInnerExceptio e a propriedade Message, que fornecem informações específicas sobre o erro que ocorreu.

Hierarquia de herança de exceção

O runtime tem um conjunto base de exceções que derivam da classe SystemException, que ele lança quando uma exceção for encontrada. A maioria das classes que herdam da classe de exceção, como a classe IndexOutOfRangeException e a classe ArgumentNullException, não implementam membros adicionais. Portanto, a informação mais importante para uma exceção pode ser encontrada na hierarquia das exceções, o nome da exceção, e as informações contidas no exceção.

Hierarquia manipulação de exceção

Em uma aplicação ASP.NET Web Forms, exceções podem ser tratadas com base em uma hierarquia de tratamento específico. Uma exceção pode ser tratada nos seguintes níveis:
  1. Nível de aplicação;
  2. Nível de página;
  3. Nível de código.
Quando um aplicativo lida com exceções, informações adicionais sobre a exceção que é herdada da classe Exception muitas vezes podem ser recuperadas e exibidas para o usuário. Além do nível de aplicativo, página e código, você também pode lidar com exceções no nível de módulo HTTP e/ou usando um manipulador personalizado IIS.

Tratamento de erros a nível de aplicação

Você pode lidar com erros padrão no nível do aplicativo, quer seja modificando a configuração de seu aplicativo ou adicionando um manipulador Application_Error no arquivoGlobal.asax de sua aplicação.
Você pode lidar com erros padrão e erros de HTTP adicionando uma seção customErrors no arquivo Web.config. A seção customErrors permite que você especifique uma página padrão para a qual os usuários serão redirecionados quando ocorrer um erro. Ele também permite que você especifique páginas individuais para erros de código específicos de status.
A seguir, temos um exemplo de configuração feita no arquivo web.config que define o tratamento de erro neste escopo:
1
2
3
4
5
6
7
<configuration>
 <system.web>
 <customErrors mode="On" defaultRedirect="ErrorPage.aspx?handler=customErrors%20section%20-%20Web.config">
 <error statusCode="404" redirect="ErrorPage.aspx?msg=404&amp;handler=customErrors%20section%20-%20Web.config"/>
 </customErrors>
 </system.web>
</configuration>
Infelizmente, quando usamos a configuração para redirecionar o usuário para uma página diferente, não temos os detalhes do erro que ocorreu.No entanto, podemos capturar os erros que ocorrem em qualquer lugar na aplicação, adicionando código para o manipulador Application_Error no arquivo Global.asax, conforme mostra o exemplo a seguir:
1
2
3
4
5
6
7
8
9
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
        ' dispara quando um erro ocorrer
        Dim exc As Exception = Server.GetLastError()
 
        If TypeOf exc Is HttpUnhandledException Then
            ' Passa o erro para a página de erro.
            Server.Transfer("ErrorPage.aspx?handler=Application_Error%20-%20Global.asax", True)
        End If
    End Sub

Tratamento de erro a nível de página

Um manipulador a nível de página retorna o usuário para a página onde o erro ocorreu, mas como as instâncias dos controles não são mantidas, não haverá mais nada na página. Para fornecer os detalhes do erro para o usuário, devemos escrever especificamente os detalhes do erro na página.
Devemos usar um manipulador de erro a nível de página para registrar erros sem tratamento ou para levar o usuário para uma página que pode exibir informações úteis.
Este exemplo de código mostra um manipulador para o evento de erro em uma página ASP.NET. Este manipulador captura todas as exceções que não estão sendo tratadas dentro do bloco try/catch na página.
1
2
3
4
5
6
7
8
9
10
Private Sub Page_Error(sender As Object, e As EventArgs)
 
Dim exc As Exception = Server.GetLastError()
' Trata o erro
If TypeOf exc Is HttpUnhandledException Then
     ErrorMsgTextBox.Text = "Ocorreu um erro nesta página "
End If
    ' LImpar ao erro a partir do servidor
    Server.ClearError()
End Sub
Depois de tratar um erro, você deve limpá-lo, chamando o método ClearError do objeto Server (classe HttpServerUtility); caso contrário, você vai ver um erro que tenha ocorrido anteriormente.

Tratamento de erro a nível de código

A instrução try-catch consiste em um bloco try seguido de uma ou mais cláusulas de captura, que especificam os manipuladores para exceções diferentes. Quando uma exceção é lançada, o Common Language Runtime (CLR) procura a instrução catch que trata esta exceção.
Se o método atualmente em execução não contém um bloco catch, a CLR olha para o método que chamou o método atual, e assim por diante, até a pilha de chamadas. Se nenhum bloco catch é encontrado, a CLR exibe uma mensagem de exceção não tratada para o usuário e para a execução do programa.
O exemplo de código a seguir mostra uma maneira comum de usar try/catch/finally para manipular erros:

1
2
3
4
5
6
7
8
9
10
11
Try
    file.ReadBlock(buffer, index, buffer.Length)
Catch e As FileNotFoundException
    Server.Transfer("NoFileErrorPage.aspx", True)
Catch e As System.IO.IOException
    Server.Transfer("IOErrorPage.aspx", True)
Finally
    If file IsNot Nothing Then
    file.Close()
   End If
End Try
No código acima, o bloco try contém o código que precisa ser protegido contra uma possível exceção. O bloco é executado até que uma exceção é lançada ou o bloco é concluído com êxito. Se uma excepção FileNotFoundException ou uma exceção IOException ocorre, a execução é transferida para uma página diferente. Em seguida, o código contido no bloco finally é executado, se ocorreu um erro ou não – o código do bloco finally sempre será executado.

Incluindo o suporte ao registro de erros

Antes de adicionar a manipulação de erros em nossa aplicação, vamos adicionar o suporte ao registro de erros, adicionando uma classe ExceptionUtility na pasta de Logic do nosso projeto.
Ao fazer isso, cada vez que o aplicativo tratar um erro, os detalhes do erro serão adicionados ao arquivo de log de erros.
Clique com o botão direito do mouse sobre a pasta Logic e selecione Add -> New Item;
A seguir, selecione Visual Basic -> Code e o template Class e informe o nome ExceptionUtility.vb e clique no botão Add;
aspn_4591
A seguir, digite o código abaixo neste arquivo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.IO
 
Namespace WingtipToys.Logic
    ' Creia o nosso utilitário de tratamenteo de exceptions
    Public NotInheritable Class ExceptionUtility
        ' Todos os métodos são estativiso
        Private Sub New()
        End Sub
 
        ' Registra uma exceção
        Public Shared Sub LogException(exc As Exception, source As String)
            ' Inclui a logica para as registrar as exceções
            ' Pega o caminho absoluto para o arquivo de log
            Dim logFile As String = "App_Data/ErrorLog.txt"
            logFile = HttpContext.Current.Server.MapPath(logFile)
 
            ' Abre o arquivo de log para acnexar e escrever o log
            Dim sw As New StreamWriter(logFile, True)
            sw.WriteLine("********** {0} **********", DateTime.Now)
            If exc.InnerException IsNot Nothing Then
                sw.Write("Inner Exception Type: ")
                sw.WriteLine(exc.InnerException.[GetType]().ToString())
                sw.Write("Inner Exception: ")
                sw.WriteLine(exc.InnerException.Message)
                sw.Write("Inner Source: ")
                sw.WriteLine(exc.InnerException.Source)
                If exc.InnerException.StackTrace IsNot Nothing Then
                    sw.WriteLine("Inner Stack Trace: ")
                    sw.WriteLine(exc.InnerException.StackTrace)
                End If
            End If
            sw.Write("Exception Type: ")
            sw.WriteLine(exc.[GetType]().ToString())
            sw.WriteLine("Exception: " + exc.Message)
            sw.WriteLine("Source: " + source)
            sw.WriteLine("Stack Trace: ")
            If exc.StackTrace IsNot Nothing Then
                sw.WriteLine(exc.StackTrace)
                sw.WriteLine()
            End If
            sw.Close()
        End Sub
    End Class
End Namespace
Quando ocorre uma exceção, ela pode ser gravada em um arquivo de log de exceção chamando o método LogException. Este método tem dois parâmetros: o objeto da exceção e uma string contendo detalhes sobre a origem da exceção. O log de exceção é gravado no arquivo ErrorLog.txt na pasta App_Data.

Adicionando uma página de erro

Em nossa aplicação usaremos uma página para exibir erros. A página de erro é projetada para mostrar uma mensagem de erro segura para os usuários do site. No entanto, se o usuário for um desenvolvedor fazendo uma solicitação HTTP que está sendo servida localmente na máquina onde reside o código, iremos exibir detalhes de erro adicionais na página de erro que serão vistas apenas pelo desenvolvedor.
Clique com o botão direito do mouse sobre o nome do projeto e selecione Add -> New Item. Selecione Visual Basic -> Web e, a seguir, o template Web Form using Master Page e informe o nome ErrorPage.aspx e clique no botão Add.
aspn_4592
Selecione o arquivo Site.Master como a página principal e, em seguida, escolha OK.
A seguir, substitua a marcação existente pelo código a seguir:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="ErrorPage.aspx.vb" Inherits="WingTipToys.ErrorPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
     <h2>Erro:</h2>
    <p></p>
    <asp:Label ID="FriendlyErrorMsg" runat="server" Text="Label" Font-Size="Large" style="color: red"></asp:Label>
 
    <asp:Panel ID="DetailedErrorPanel" runat="server" Visible="false">
        <p>
            Erro Detalhado:
            <br />
            <asp:Label ID="ErrorDetailedMsg" runat="server" Font-Bold="true" Font-Size="Large" /><br />
        </p>
        <p>
            Tratamento Erro:
            <br />
            <asp:Label ID="ErrorHandler" runat="server" Font-Bold="true" Font-Size="Large" /><br />
        </p>
        <p>
            Mensagem de erro Detalhada:
            <br />
            <asp:Label ID="InnerMessage" runat="server" Font-Bold="true" Font-Size="Large" /><br />
        </p>
        <pre>
            <asp:Label ID="InnerTrace" runat="server"  />
        </pre>
    </asp:Panel>
 
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
</asp:Content>
No arquivo code-behind ErrorPage.aspx.vb inclua o código abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports WingTipToys.WingtipToys.Logic
 
Public Class ErrorPage
    Inherits System.Web.UI.Page
 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        ' Cria as mensagens de erro seguras
        Dim generalErrorMsg As String = "Ocorreu um problema na aplicação web. Tente novamente. " + "Se o ocorre persistir contacte o suporte técnico."
        Dim httpErrorMsg As String = "Ocorreu um erro HTTP. Página não encontrada. Tente novamente."
        Dim unhandledErrorMsg As String = "O erro não foi tratado pelo código da aplicação."
 
        ' exibe as mensagens de erro
        FriendlyErrorMsg.Text = generalErrorMsg
 
        ' Determina onde o erro foi tratado
        Dim errorHandler__1 As String = Request.QueryString("handler")
        If errorHandler__1 Is Nothing Then
            errorHandler__1 = "Error Page"
        End If
 
        ' pegao ultimo erro do servidor.
        Dim ex As Exception = Server.GetLastError()
 
        ' Pega o numero do erro passado como um valor querystring
        Dim errorMsg As String = Request.QueryString("msg")
        If errorMsg = "404" Then
            ex = New HttpException(404, httpErrorMsg, ex)
            FriendlyErrorMsg.Text = ex.Message
        End If
 
        'Se a exceção não existe mais, cria uma exceção genérica
        If ex Is Nothing Then
            ex = New Exception(unhandledErrorMsg)
        End If
 
        ' exibe os detalhes de erro somente para o desenvolvedor.SOMENTE ACESSO LOCAL
        If Request.IsLocal Then
            'mensagem de erro detalhada
            ErrorDetailedMsg.Text = ex.Message
 
            ' mostra onde o erro foi tratado
            ErrorHandler.Text = errorHandler__1
 
            ' mostra o acesso local
            DetailedErrorPanel.Visible = True
 
            If ex.InnerException IsNot Nothing Then
                InnerMessage.Text = ex.[GetType]().ToString() + "<br/>" + ex.InnerException.Message
                InnerTrace.Text = ex.InnerException.StackTrace
            Else
                InnerMessage.Text = ex.[GetType]().ToString()
                If ex.StackTrace IsNot Nothing Then
                    InnerTrace.Text = ex.StackTrace.ToString().TrimStart()
                End If
            End If
        End If
 
        ' registra a exceção
        ExceptionUtility.LogException(ex, errorHandler__1)
 
        ' limpa o erro do servidor
        Server.ClearError()
    End Sub
 
End Class
Quando a página de erro for exibida, o evento Page_Load é executado. No manipulador Page_Load, o local onde o erro foi primeiro tratado é determinado. Então, o último erro que ocorreu é obtido através do método GetLastError do objeto Server.
Se a exceção não existir mais, uma exceção genérica será criada. Então, se o pedido HTTP foi feita localmente, todos os detalhes do erro serão mostrados. Neste caso, apenas a máquina local executando a aplicação web vai ver esses detalhes do erro. Depois que as informações do erro forem exibidas, o erro é adicionado ao arquivo de log e o erro é eliminado do servidor.

Exibindo mensagens de erro não tratadas para a aplicação

Ao adicionar uma seção customErrors no arquivo Web.config, você pode rapidamente tratar erros simples que ocorrem na aplicação. Você também pode especificar como lidar com erros com base no seu valor de código de status, como 404 – Arquivo não encontrado.
Abra o arquivo web.config da raiz do projeto e inclua a seção customErrors no interior do nó conforme abaixo:
aspn_4593
A seção customErrors especifica o modo, o que é definido como “On” (mode=”On”). Também especifica o defaultRedirect, que diz à aplicação para qual página navegar quando ocorrer um erro.
Além disso, adicionamos um elemento de erro específico que define como lidar com um erro 404 quando uma página não for encontrada. Mais adiante, vamos adicionar o tratamento de erro adicional que irá capturar os detalhes de um erro a nível de aplicação.

Testando a aplicação

Execute a aplicação pressionando CTRL+F5 (ou clicando no botão do menu que executa a aplicação). O navegador irá abrir e apresentar a página Default.aspx:
aspn_4584 (1)
Informe a seguinte URL no navegador para provocar um erro: http://localhost:674846/NoPage.aspx (o número da porta para sua aplicação será diferente).
A página de erro ErrorPage.aspx será exibida conforme a figura a seguir:
aspn_4594
Quando você solicita a página NoPage.aspx que não existe, a página de erro irá mostrar a mensagem de erro simples e as informações de erro detalhadas (se detalhes adicionais estiverem disponíveis).
No entanto, se o usuário solicitou uma página inexistente a partir de um local remoto, a página de erro só vai mostrar a mensagem de erro em vermelho.

Incluindo uma exceção para testes

Para verificar como o aplicativo irá funcionar quando ocorre um erro, você pode deliberadamente criar condições de erro. Vamos lançar uma exceção de teste quando a página padrão for carregada para ver o que acontece.
Abra o arquivo code-behind Default.aspx.vb e no evento Load da página inclua o código abaixo:
1
2
3
4
5
6
7
Public Class _Default
    Inherits Page
 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        Throw New InvalidOperationException("O Erro : InvalidOperationException " + "õcorreu no evento Page_Load ná página Default.aspx.")
    End Sub
End Class
No código acima estamos criando uma exceção do tipo InvalidOperationException, que irá ocorrer quando a página Default.aspx for carregada.

Testando a aplicação

Execute a aplicação novamente pressionando CTRL+F5 (ou clicando no botão do menu que executa a aplicação). O navegador irá abrir e apresentar a página Default.aspx.
A página exibirá a mensagem de erro conforme abaixo:
aspn_4596

Adicionando o tratamento de erros a nível de aplicativo

Em vez de capturar a exceção usando a seção customErrors no arquivo Web.config, onde você ganha pouca informação sobre a exceção, você pode interceptar o erro no nível da aplicação e recuperar detalhes do erro.
Abra o arquivo Global.asax e atualize o código do evento Application_Error conforme abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
       ' pega o último erro do servidor
       Dim exc As Exception = Server.GetLastError()
 
       If TypeOf exc Is HttpUnhandledException Then
           If exc.InnerException IsNot Nothing Then
               exc = New Exception(exc.InnerException.Message)
               Server.Transfer("ErrorPage.aspx?handler=Application_Error%20-%20Global.asax", True)
           End If
       End If
 
   End Sub
Quando ocorre um erro na aplicação, o manipulador Application_Error é chamado. Nesse manipulador, a última exceção é recuperada e revisada. Se a exceção ocorreu sem um tratamento e a exceção contém detalhes de uma exceção interior (InnerException não é nulo), a aplicação transfere a execução para a página de erro onde os detalhes da exceção são exibidos.

Testando a aplicação

Execute a aplicação novamente pressionando CTRL+F5 (ou clicando no botão do menu que executa a aplicação). O navegador irá abrir e apresentar a página Default.aspx.
A página exibirá a mensagem de erro conforme abaixo:
aspn_4595

Adicionando o tratamento de erros a nível de página

Podemos adicionar a manipulação de erros a nível de página para uma página, seja adicionando um atributo ErrorPage na diretiva @Page da página, ou pela adição de um manipulador de eventos Page_Error ao código-behind da página. Vamos adicionar um manipulador de eventos Page_Error que irá transferir a execução para a página ErrorPage.aspx.
Abra o arquivo code-behind Default.aspx.vb e inclua o código para o evento Page_Error() conforme abaixo:
1
2
3
4
5
6
7
8
9
10
Private Sub Page_Error(sender As Object, e As EventArgs)
        ' pega o ultimo erro do servidor.
        Dim exc As Exception = Server.GetLastError()
 
        ' trata o erro
        If TypeOf exc Is InvalidOperationException Then
            ' Passa o erro para pagina de erro
            Server.Transfer("ErrorPage.aspx?handler=Page_Error%20-%20Default.aspx", True)
        End If
    End Sub
Quando ocorre um erro na página, o manipulador de eventos Page_Error é chamado. Nesse manipulador, a última exceção é recuperada e revisada. Se um erro do tipo InvalidOperationException ocorrer, o manipulador de eventos Page_Error transfere a execução para a página de erro onde os detalhes da exceção são exibidos.

Testando a aplicação

Execute a aplicação novamente pressionando CTRL+F5 (ou clicando no botão do menu que executa a aplicação). O navegador irá abrir e apresentar a página Default.aspx.
A página exibirá a mensagem de erro conforme abaixo:
aspn_4597
Após essa bateria de testes, remova ou comente o código usado no evento Load da página Default.aspx para lançar deliberadamente um erro.

Usando ELMAH

ELMAH (módulos de registro de erros e manipuladores) é uma unidade de registro de erros que você conecta em sua aplicação ASP.NET como um pacote NuGet. A ELMAH oferece os seguintes recursos:
  • Registro de exceções não tratadas;
  • Uma página da Web para exibir todo o log de exceções não tratadas;
  • Uma página da Web para exibir os detalhes de cada exceção logada;
  • Uma notificação por e-mail de cada erro no momento em que ocorre;
  • Um feed RSS dos últimos 15 erros do registro.
Antes de trabalhar com o ELMAH, temos que instalá-lo. Isto é fácil usando o instalador de pacotes NuGet.
Obs: O NuGet é uma extensão do Visual Studio que faz com que seja fácil de instalar e atualizar bibliotecas de código aberto e ferramentas no Visual Studio.
Com o projeto aberto no Visual Web Developer for Web, clique no menu TOOLS  e a seguir selecione Library Package Manager -> Manage Nuget Packages for Solution.
aspn_4598
A janela - Manage Nuget Packages - será exibida. Selecione o item OnLine e digite na caixa de busca a palavra ELMAH. O pacote ELMAH deverá ser exibido no topo da lista de itens encontrados. Clique no botão Install para instalar o pacote ELMAH. A instalação apresentará uma tela solicitando a confirmação para instalar o pacote no projeto. Confirme clicando em OK.
Obs: Você deve estar conectado para poder baixar o pacote.
aspn_4599
Se for solicitado, recarregar os arquivos abertos, selecione “Sim para Todos” (Yes to All).
O pacote ELMAH adiciona entradas na seção <modules> do nó <system.webServer> no arquivo Web.config. Se o sistema solicitar se você deseja recarregar o arquivo Web.config modificado, clique em Sim(Yes).
Com essa etapa concluída, estamos pronto para usar o pacote ELMAH.

Visualizando o log ELMAH

Ver o log ELMAH é fácil, mas primeiro vamos criar uma exceção não tratada que será gravada no log ELMAH.
Pressione CTRL + F5 para executar nossa aplicação. A seguir, digite a seguinte URL (usando o seu número de porta) no navegador para provocar uma exceção não tratada no log ELMAH: localhost:64786/NoPage.aspx.
aspn_459a
A página de erro será exibida conforme abaixo:
aspn_459b
Para visualizar o log de erros ELMAH digite a url no navegador: http://localhost:64786/elmah.axd
aspn_459c
O log de erros ELMAH será exibido indicando informações sobre a ocorrência:
aspn_459d
Se você clicar no link Details, irá obter os detalhes da exceção:
aspn_459e
Atenção!!! Cuidado ao usar o recurso ELMAH, pois qualquer um pode ler o código de erro acessando a página, e existem exploits (um exploit é um programa de computador ou uma sequência de comandos que se aproveita das vulnerabilidades de um sistema) que exploram isso.