Quantcast
Channel: José Carlos Macoratti
Viewing all 293 articles
Browse latest View live

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

$
0
0

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.

Pegue o projeto completo aqui: Mvc4PayPal.zip (exclui o pacote com as referências para diminuir o tamanho do download)

O post ASP .NET MVC – Fazendo a integração com o PayPal apareceu primeiro em .


WCF – Consumindo um serviço com dados no SQL Server (VB.NET)

$
0
0

Este artigo apresenta como consumir um serviço que consulta dados no SQL Server 2008 usando a linguagem VB .NET.

Para consultar dados em um banco de dados SQL Server usando um serviço WCF, devemos fazer realizar as seguintes ações:

  • Criar o banco de dados e a tabela no SQL Server(ou usar um banco de dados e tabelas existentes);
  • Criar um WCF Service;
  • Criar um aplicativo web que usará o serviço WCF.

Na primeira etapa, vamos utilizar um banco de dados existente no SQL Server, depois disso criamos uma função simples para consultar dados no banco de dados usando um serviço WCF.

A seguir, criaremos uma aplicação web onde vamos incluir uma referência ao serviço e aos dados a serem consultados e exibidos em um controle gridivew.

A aplicação exemplo foi desenvolvida no Visual Web Developer 2010 Express(VWD 2010) e no SQL Server 2008.

A tecnologia WCF - Windows Communication Foundation surgiu com a .NET Framework 3.0, com o objetivo de unificar as até então existentes tecnologias de programação distribuídas, como COM+ , MSMQ-Message Queue, Enterprise Services, .NET Remoting e Web Services.

Com o advento da WCF foi criada uma plataforma com uma API que facilitou de forma considerável o desenvolvimento de aplicações distribuídas, visto que o WCF não está acoplado às regras de negócio que deverão ser expostas pelo serviço.

Para iniciar com o WCF existem alguns conceitos básicos que você deve conhecer para projetar, implementar e hospedar os seus serviços. Na verdade, os templates WCF fornecidos noVisual Studio simplificam muito este processo, visto que eles fornecem um serviço modelo que pode ser imediatamente hospedado e testado com as ferramentas de teste WCF. Assim, chamar um serviço WCF a partir de um cliente é também uma tarefa simples, basta gerar um proxy e escrever o código contra o seu modelo de objetos.

O banco de dados e a tabela

Usaremos o banco de dados chamado Escola e a tabela Usuarios, cuja estrutura e dados são exibidas abaixo:

wcf_acd1

Criando o serviço WCF

A seguir, crie o projeto WCF, usando o VWD 2010, com o nome WcfService_GDV_VB. Abra o VWD 2010 Express e no menu File clique em New Project; depois selecione a linguagem C# e o template WCF e a seguir WCF Service Application. Informe o nome WcfService_InserirDados_SQLServer e clique em OK:

wcf_acd2

Será criado um projeto com a estrutura abaixo mostrada na janela Solution Explorer:

 wcf_acd3

Vamos definir o contrato e o contrato de dados na interface IService conforme o código a seguir. Lembre-se que a interface define apenas as assinaturas dos métodos que deverão ser implementados por uma classe concreta!

uImports System.Runtime.Serialization
Imports System.ServiceModel
Imports System.Data

Namespace WcfService_GDV
    <ServiceContract()> _
    Public Interface IService1
        <OperationContract()> _
        Function GetUsuarios() As Usuario
    End Interface

    <DataContract()> _
    Public Class Usuario
        <DataMember()> _
        Public Property tabelaUsuario() As DataTable
            Get
                Return m_tabelaUsuario
            End Get
            Set(ByVal value As DataTable)
                m_tabelaUsuario = Value
            End Set
        End Property
        Private m_tabelaUsuario As DataTable
    End Class

End Namespace

A primeira etapa para criar um serviço WCF e efetuar a definição do contrato, pois é o contrato que vai definir quais operações serão expostas pelo serviço, que informações são necessárias para que essas operações sejam executadas e qual o tipo de retorno esperado.

Obs: O contrato é uma interface que contém as assinaturas dos métodos que serão expostos. A interface deverá ser decorada com o atributo:ServiceContract.

  • Contratos de serviços (Service Contracts) – Descrevem as operações que um serviço pode realizar e mapeia os tipos CLR para WSDL.
  • Contratos de Dados (Data Contracts) – Descreve a estrutura de dados usada no serviço (mapeia tipos CLR para XSD). Um Data Contract é um acordo formal entre um serviço e um cliente que descreve os dados que serão trocados entre ambos. Para estabelecer a comunicação, o cliente e o serviço não necessitam trocar necessariamente os mesmos tipos de dados; eles devem trocar apenas os mesmos data contracts.

Um Data Contract especifica para cada parâmetro ou tipo de retorno qual informação será serializada (convertida em XML) para ser trocada. Os DataContracts são definidos através de classes e uma classe DataContract deverá ser decorada com o atributo DataContract e os campos e propriedades que o tipo possui devem ser decorados com o atributo DataMember. A partir do service pack versão 3.5 da plataforma .NET isso não é mais obrigatório

Definimos o contrato de serviço: GetUsuarios e o contrato de dados Usuario.

Neste processo, realizamos as seguintes tarefas:

  • Definimos um contrato. (Uma Interface comum);
  • Implementamos o contrato. (Usamos uma Classe que implementa a interface);
  • Hospedamos o serviço. (VS hospeda o serviço no IIS local);
  • Referenciamos o serviço e chamamos o serviço a partir do cliente.(aplicação web).

Agora vamos implementar o método GetUsuarios na classe Service1 no arquivo code-behind Service.svc digitando o código conforme abaixo:

Imports System.Configuration
Imports System.Data.SqlClient
Imports System.Data

Namespace WcfService_GDV
    Public Class Service1
        Implements IService1

        Private conString As String = ConfigurationManager.ConnectionStrings("conexaoSQL").ConnectionString
        Private con As SqlConnection
        Private cmd As SqlCommand
        Private da As SqlDataAdapter
        Private dt As DataTable

        Private usuario As New Usuario()

        Public Function GetUsuarios() As Usuario Implements IService1.GetUsuarios
            Using con = New SqlConnection(conString)
                cmd = New SqlCommand("Select top 10 * from Usuarios", con)
                da = New SqlDataAdapter(cmd)
                dt = New DataTable("Paging")
                da.Fill(dt)
                usuario.tabelaUsuario = dt
                Return usuario
            End Using
        End Function
    End Class
End Namespace

Observe que estamos referenciando o namespace System.Configuration; para isso temos que incluir uma referência no projeto web via menu Project -> Add Reference;

Selecionando, em seguida, a guia .NET e o item System.Configuration:

wcf_acd4

Não podemos esquecer de definir a string de conexão com o banco de dados no arquivo web.config, conforme abaixo:

<?xml version="1.0"?>
<configuration>

  <connectionStrings>
    <add
      name="conexaoSQL"
      connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Escola;Integrated Security=True;" />
  </connectionStrings>

  <system.web>
  .....

Agora pressione F5 para executar o serviço. Um formulário WCF Test Client será exibido e vai executar o serviço.

wcf_addb

Teremos a exibição do serviço e do método GetUsuarios() definido:

wcf_acd5

Após isso, o serviço foi adicionado com êxito.

Vamos agora abrir o serviço no navegador. Para isso, com o botão direito do mouse, clique sobre o arquivo service1.vcs e a seguir em View in Browser:

wcf_acd6

Copie a URL : http://localhost:61404/Service1.svc (no seu exemplo provavelmente a porta será diferente) para usá-la mais adiante.

Criando a aplicação WEB

Vamos criar a aplicação WEB que irá consumir o serviço WCF criado.

No menu File clique em Add -> New Project, selecione o template ASP .NET Web Application e informe o nome ConsultarDadosWeb:

wcf_acd7

Vamos incluir no projeto web a referência ao serviço WCF.

Clique com o botão direito do mouse sobre o Projeto Web e selecione o item Add Service Reference:

wcf_add6

Será aberta a janela mostrada abaixo:

wcf_add7

Vamos colar a URL que copiamos quando abrimos o serviço no navegador na caixa de texto Address e clicar no botão Go:

wcf_acd8

Deveremos ver o serviço criado e na área Operations o método GetUsuarios do serviço.

Clique no botão OK para incluir a referência no projeto:

wcf_acd9

Agora, abra a página Default.aspx e inclua um controle GridView a partir da ToolBox:

wcf_acda

A seguir, no evento Load da página, inclua o código abaixo, que irá instanciar o serviço criado e usar o método exposto para inserir dados no SQL Server.

Public Class _Default
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim servico As New ServiceReference1.Service1Client()
        Dim _usuario As New ServiceReference1.Usuario()
        _usuario = servico.GetUsuarios()
        Dim dt As New DataTable()
        dt = _usuario.tabelaUsuario
        GridView1.DataSource = dt.DefaultView
        GridView1.DataBind()
    End Sub

End Class

Obtemos os dados do formulário e após criar uma instância do serviço usamos o método GetUsuarios para obter as informações da tabela e exibir o resultado no controle GridView.

Execute a aplicação e a tela abaixo será apresentada:

wcf_acdb

Vimos assim como é simples usar os recursos do WCF para obter informações no SQL Server.

Em outro artigo irei mostrar como realizar as operações CRUD usando um serviço WCF.

Pegue o projeto completo aqui: WcfService_GDV_VB.zip

O post WCF – Consumindo um serviço com dados no SQL Server (VB.NET) apareceu primeiro em .

VB.NET – Criando um projeto para distribuição com o Crystal Reports

$
0
0

Hoje veremos como criar um projeto para distribuir uma aplicação VB .NET que utiliza o Crystal Reports usando o Visual Studio 2010.

Após o desenvolvimento de qualquer aplicação, a próxima tarefa é a distribuição do aplicativo e sua implantação nos usuários finais. O Visual Studio possui um módulo interno de instalação e implantação para a criação de pacotes de instalação.

Neste artigo, vou mostrar como criar o seu próprio pacote de instalação, que deve ser capaz de instalar o aplicativo de exemplo em qualquer computador com Windows. O nosso aplicativo exemplo é um projeto Windows Forms, que carrega um arquivo de relatório do Cristal Reports. Ao final deste artigo, você deve ser capaz de implantar o projeto de exemplo, o Microsoft. NET Framework e Runtime Crystal Report na máquina de destino.

Como o objetivo do artigo não é mostrar como criar relatórios com o Crystal Reports, eu vou partir de um projeto VB .NET que já possui um relatório criado usando essa ferramenta. Se você desejar criar o relatório passo a passo veja como fazer isso neste artigo.

Abra o Visual Studio 2010 e no menu File clique em New Project e selecione o template Windows Forms informando o nome VBNET_Crystal.

A seguir, crie um relatório usando o Crystal Reports, conforme as instruções do artigo citado acima. Com o projeto completo e testado, estamos prontos para a distribuição do mesmo aos usuários.

Criando o projeto para distribuição da aplicação

No menu File, clique em Add -> New Project. Na janela Add New Project clique em Other Project Types e a seguir em Setup and Deployment.

Depois, clique em Visual Studio Installer e selecione Setup Project informando um nome sugestivo. Eu vou aceitar o nome padrão Setup1.

Clique no botão OK:

vbn_stp12

Será criado um novo projeto na solução com o nome Setup1, conforme figura abaixo:

vbn_stp13

Clique com o botão direito do mouse sobre o projeto Setup1. A seguir, clique em Add -> File:

vbn_stp14

Localize os arquivos do Crystal Reports e o arquivo .EXE da aplicação – eles devem estar no projeto na pasta \bin\Release.

O exemplo mostra a pasta \bin\Debug, pois é apenas um demo, mas a distribuição final deve ser uma versão Release.

vbn_stp15

Os arquivos deverão ser adicionados ao projeto Setup, conforme mostrado a seguir:

vbn_stp16

Clique agora com o botão direito do mouse sobre projeto Setup1 e, a seguir, clique em Properties:

vbn_stp17

 

Na janela Property Pages clique no botão Prerequisites:

Na janela Prerequisites marque as seguinte opções:

  • Microsoft .NET Framework 4 (x86 and 64);
  • Windows Installer 3.1;
  • Microsoft Visual Studio 2010 Report Viewer;
  • SAP Crystal Reports RunTime Engine for .NET Framework 4.0.

vbn_stp18

Marque também a opção: download prerequisites from the component vendor’s web site

Clique no botão OK. Para adicionar o atalho menu do programa, clique com o botão direito sobre o arquivo EXE e a seguir clique em ”Create ShortCut to…” no menu de contexto.

vbn_stp19

A seguir, arraste e solte o atalho criado para a pasta ”User´s Programs Menu”:

vbn_stp1a

Agora, clique no menu Build -> Build Solution para criar o pacote de instalação.

Na pasta do projeto, copie todos os arquivos localizados em bin\Release (ou \bin\Debug para o nosso caso) para um CD ou drive USB. Para testar o projeto clique em Setup.exe.

Os pacotes necessários ao funcionamento da aplicação com o Crystal Report serão baixados e instalados a partir do site do distribuidor do Crystal a empresa SAP.

Aguarde em breve mais artigos sobre o Crystal Reports.

Pegue o projeto completo aqui: VBNET_Crystal.zip

O post VB.NET – Criando um projeto para distribuição com o Crystal Reports apareceu primeiro em .

ASP .NET 4.5 Web Forms – Incluindo o carrinho de compras – Parte 05

$
0
0

Este artigo descreve a lógica de negócios necessária para adicionar um carrinho ao nosso site ASP .NET Web Forms. Quando você tiver concluído este processo, os usuários serão capazes de adicionar, remover e modificar os produtos em seu carrinho de compras.

O que você vai aprender:

  • Como criar um carrinho de compras para a aplicação web
  • Como permitir aos usuários adicionar itens ao carrinho de compras;
  • Como adicionar um controle GridView para exibir os detalhes do carrinho de compras;
  • Como calcular e exibir o total do pedido;
  • Como remover e atualizar os itens no carrinho de compras;
  • Como incluir um contador de carrinho de compras.

Os recursos introduzidos neste artigo são:

  • Entity Framework Code First;
  • Data Annotations;
  • Strongly typed data controls;
  • Model binding.

Este artigo foi integralmente baseado no artigo original: http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/shopping-cart (com algumas alterações e portado para a linguagem VB .NET)

Criando um carrinho de compras

Em artigo anteriores adicionamos páginas e códigos para visualizar os dados do produto a partir de um banco de dados. Hoje, vamos criar um carrinho de compras para gerir os produtos que os usuários estão interessados em comprar. Os usuários serão capazes de procurar e adicionar itens ao carrinho de compras, mesmo que não esteja registrado ou logado.

Para gerenciar o acesso ao carrinho de compras, vamos atribuir aos usuários uma identificação única, usando um identificador exclusivo (GUID), quando o usuário acessa o carrinho pela primeira vez. Vamos armazenar esse ID utilizando o estado da sessão ASP.NET.

O estado da sessão no ASP.NET é um local conveniente para armazenar informações específicas do usuário que irá expirar após o usuário deixar o site. Embora uso indevido de estado da sessão pode ter implicações de desempenho em sites maiores, a sua utilização com moderação funciona bem para fins de demonstração.

Adicionando um Item do Carrinho (CarrinhoItem) como uma classe do modelo

Já definimos em nossa aplicação o esquema para a categoria e dados do produto, criando classes para representar as categorias e os produtos na pasta Models.

Vamos, agora, incluir uma nova classe para definir o esquema para o carrinho de compras. Posteriormente, vamos incluir uma classe para manipular os dados de acesso a tabelaCarrinhoItem. Esta classe irá fornecer a lógica de negócio para adicionar, remover e atualizar itens no carrinho de compras.

Na janela Solution Explorer clique com o botão direito do mouse sobre a pasta Models e selecione Add -> New Item;

Na janela Add New Item selecione Visual Basic -> Code e o template Class informando o nome CarrinhoItem.vb e clicando no botão Add;

aspn_4551

A seguir, digite o código abaixo neste arquivo:

Imports System.ComponentModel.DataAnnotations

Namespace WingtipToys.Models
    Public Class CarrinhoItem
        <Key> _
        Public Property ItemId() As String
            Get
                Return m_ItemId
            End Get
            Set(value As String)
                m_ItemId = value
            End Set
        End Property
        Private m_ItemId As String

        Public Property CarrinhoId() As String
            Get
                Return m_CarrinhoId
            End Get
            Set(value As String)
                m_CarrinhoId = value
            End Set
        End Property
        Private m_CarrinhoId As String

        Public Property Quantidade() As Integer
            Get
                Return m_Quantidade
            End Get
            Set(value As Integer)
                m_Quantidade = value
            End Set
        End Property
        Private m_Quantidade As Integer

        Public Property DataCriacao() As System.DateTime
            Get
                Return m_DataCriacao
            End Get
            Set(value As System.DateTime)
                m_DataCriacao = value
            End Set
        End Property
        Private m_DataCriacao As System.DateTime

        Public Property ProdutoId() As Integer
            Get
                Return m_ProdutoId
            End Get
            Set(value As Integer)
                m_ProdutoId = value
            End Set
        End Property
        Private m_ProdutoId As Integer

        Public Overridable Property Produto() As Produto
            Get
                Return m_Produto
            End Get
            Set(value As Produto)
                m_Produto = value
            End Set
        End Property
        Private m_Produto As Produto
    End Class
End Namespace

A classe CarrinhoItem possui seis propriedades:

  • ItemId
  • CarrinhoId
  • Quantidade
  • DataCriacao
  • ProdutoId
  • Produto

A classe carrinhoItem contém o esquema que vai definir cada produto que o usuário adiciona ao carrinho de compras. Esta classe é igual as demais classes de esquema que criamos anteriormente.

Por convenção, o Entity Framework Code-First espera que a chave primária da tabela Carrinhotem seja ou CarrinhoItemId ou ID. No entanto, o código sobrescreve o comportamento padrão usando o atributo data annotation [Key]. O atributo Key da propriedade ItemId especifica que a propriedade ItemID é a chave primária.

A propriedade CarrinhoId especifica o ID do usuário que está associado com o item para comprar. Você vai adicionar código para criar esse ID de usuário quando o usuário acessa o carrinho de compras. Este ID também será armazenado como uma variável de sessão ASP.NET.

Atualizando o contexto produto

Além de adicionar a classe CarrinhoItem, vamos precisar atualizar a classe de contexto de banco de dados que gerencia as classes de entidade e que fornece acesso a dados para o banco de dados. Para fazer isso, vamos adicionar a classe carrinhoItem à classe ProdutoContexto.

Na janela Solution Explorer, abra a pasta Models e selecione e abra o arquivo ProdutoContexto.vb. A seguir, inclua a linha de código: Public Property CarrinhoItens() As DbSet(Of CarrinhoItem) conforme mostrada abaixo:

Imports System.Data.Entity

Namespace WingtipToys.Models

    Public Class ProdutoContexto
        Inherits DbContext

        Public Sub New()
            MyBase.New("WingtipToys")
        End Sub

        Public Property Categorias() As DbSet(Of Categoria)
        Public Property Produtos() As DbSet(Of Produto)
        Public Property CarrinhoItens() As DbSet(Of CarrinhoItem)

    End Class
End Namespace

O código do arquivo ProdutoContexto.vb adiciona o namespace System.Data.Entity para que tenhamos acesso a todo o núcleo de funcionalidade do Entity Framework. Esta funcionalidade inclui a capacidade de consultar, inserir, atualizar e excluir dados, trabalhando com objetos fortemente tipados. A classe ProdutoContexto também acrescenta acesso à classe CarrinhoItem.

Gerenciando a lógica de negócios do carrinho de compras

Vamos criar a classe CarrinhoCompras em uma nova pasta. Esta classe vai manipular o acesso aos dados da tabela CarinhoItem e também incluir a lógica de negócio para adicionar, remover e atualizar os itens no carrinho de compras.

A lógica de carrinho de compras que você irá adicionar conterá a funcionalidade para gerenciar as seguintes ações:

  • Adicionar itens ao carrinho de compras;
  • Remover itens do carrinho de compras;
  • Obter o ID do carrinho de compras;
  • Recuperar itens do carrinho de compras;
  • Totalizar o montante de todos os itens de carrinho de compras;
  • Atualizar os dados do carrinho de compras.

A página do carrinho de compras (Carrinho.aspx) e a classe carrinho de compras serão utilizados em conjunto para acessar os dados do carrinho. A página do carrinho de compras irá exibir todos os itens que o usuário adiciona.

Além da página do carrinho de compras e de classe, vamos criar uma página (IncluirNoCarrinho.aspx) para adicionar produtos ao carrinho de compras. Vamos adicionar o código à página ProdutoLista.aspx e na página ProdutoDetalhes.aspx, que irá fornecer um link para a página IncluirNoCarrinho.aspx, de modo que o usuário possa adicionar produtos ao carrinho de compras.

O diagrama a seguir mostra o processo básico que ocorre quando o usuário adiciona um produto no carrinho de compras.

aspn_4552

Quando o usuário clicar no link Adicionar ao carrinho de compras, na página ProdutoLista.aspx ou na página ProdutoDetalhes.aspx, o aplicativo irá navegar até a páginaIncluirNoCarrinho.aspx e automaticamente para a página Carrinho.aspx. A página IncluirNoCarrinho.aspx irá adicionar o produto selecionado no carrinho, chamando um método na classe Carrinho e a página Carrinho.aspx exibirá os produtos que foram adicionados ao carrinho de compras.

Criando a classe CarrinhoActions

A classe CarrinhoActions será adicionada a uma pasta separada no aplicativo, de modo que haverá uma distinção clara entre o modelo (pasta Models), as páginas (pasta raíz) e lógica (pasta Logic).

Clique com o botão direito sobre o nome do projeto e selecione a opção Add New Folder; informe nome da pasta como Logic e clique com o botão direito sobre a pasta Logic, selecionando Add-> New Item. Inclua uma nova classe chamada CarrinhoActions.vb e substitua o código pelo código a seguir:

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports WingTipToys.WingtipToys.Models

Namespace WingtipToys.Logic

    Public Class CarrinhoActions
        Public Property CarrinhoId() As String
            Get
                Return m_CarrinhoId
            End Get
            Set(value As String)
                m_CarrinhoId = value
            End Set
        End Property
        Private m_CarrinhoId As String

        Private _db As New ProdutoContexto()

        Public Const CarrinhoSessaoChave As String = "CarrinhoId"

        Public Sub IncluirNoCarrinho(id As Integer)
            ' Retorna o produto do banco de dados
            CarrinhoId = GetCarrinhoId()

            Dim carrinhoItem = _db.CarrinhoItens.SingleOrDefault(Function(c) c.CarrinhoId = CarrinhoId AndAlso c.ProdutoId = id)
            If carrinhoItem Is Nothing Then
                ' Cria um novo item no carrinho se não existir
                carrinhoItem = New CarrinhoItem() With { _
                    .ItemId = Guid.NewGuid().ToString(), _
                    .ProdutoId = id, _
                    .CarrinhoId = CarrinhoId, _
                    .Produto = _db.Produtos.SingleOrDefault(Function(p) p.ProdutoID = id), _
                    .Quantidade = 1, _
                    .DataCriacao = DateTime.Now _
                }
                _db.CarrinhoItens.Add(carrinhoItem)
            Else
                ' se o item não existe acrescenta um unidade
                carrinhoItem.Quantidade += 1
            End If
            _db.SaveChanges()
        End Sub

        Public Function GetCarrinhoId() As String
            If HttpContext.Current.Session(CarrinhoSessaoChave) Is Nothing Then
                If Not String.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name) Then
                    HttpContext.Current.Session(CarrinhoSessaoChave) = HttpContext.Current.User.Identity.Name
                Else
                    ' Gera um novo GUID aleatorio usado usando a classe System.Guid
                    Dim tempCartId As Guid = Guid.NewGuid()
                    HttpContext.Current.Session(CarrinhoSessaoChave) = tempCartId.ToString()
                End If
            End If
            Return HttpContext.Current.Session(CarrinhoSessaoChave).ToString()
        End Function

        Public Function GetCarrinhoItems() As List(Of CarrinhoItem)
            CarrinhoId = GetCarrinhoId()
            Return _db.CarrinhoItens.Where(Function(c) c.CarrinhoId = CarrinhoId).ToList()
        End Function
    End Class
End Namespace

O método IncluirNoCarrinho permite que os produtos individuais a sejam incluídos no carrinho de compras com base na identificação do produto. O produto será adicionado ao carrinho mas se o carrinho já contiver um item para o produto, terá sua quantidade incrementada em uma unidade.

O método GetCarrinhoId retorna o ID do carrinho, que é usado para rastrear os itens que um usuário tem em seu carrinho de compras. Se o usuário não tiver o ID do carrinho existente, um novo ID será criado. Se o usuário estiver conectado como um usuário registrado, a identificação do carrinho está definida como sendo o seu nome de usuário. No entanto, se o usuário não estiver conectado, o ID do carrinho será definido para um valor único (um GUID). Um GUID garante que apenas um carrinho será criado para cada usuário, com base na sua sessão.

O método GetCarrinhotItens retorna uma lista de itens do carrinho de compras para o usuário. Mais adiante veremos que o model binding será usado para exibir os itens dos no carrinho usando o método GetCarrinhoItens.

Incluindo itens no carrinho

Vamos criar uma página de processamento chamada IncluirNoCarrinho.aspx, que será usada para adicionar novos produtos no carrinho de compras do usuário. Esta página irá chamar o método IncluirNoCarrinho na classe Carrinho que acabamos de criar. A página InclulirNoCarrinho.aspx vai esperar que um ID do produto seja passada a ela. Este ID de produto será usado ao chamar o método IncluirNoCarrinho na classe Carrinho.

Na janela Solution Explorer clique com o botão direito do mouse sobre o projeto e selecione Add e a seguir New Item.

Inclua uma página Web Form com o nome IncluirNoCarrinho.aspx a partir da janela Add new Item;

aspn_4553 (1)

A seguir, selecione o arquivo IncluirNoCarrinho.aspx e clique com o botão direito sobre ele selecionando View Code e definindo o código abaixo no code-behind:

Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Diagnostics
Imports WingTipToys.WingtipToys.Logic

Namespace WingtipToys

    Partial Public Class IncluirNoCarrinho
        Inherits System.Web.UI.Page

        Protected Sub Page_Load(sender As Object, e As EventArgs)
            Dim rawId As String = Request.QueryString("ProdutoID")
            Dim produotId As Integer
            If Not [String].IsNullOrEmpty(rawId) AndAlso Integer.TryParse(rawId, produotId) Then
                Dim usuariosCarrinhoCompras As New CarrinhoActions()
                usuariosCarrinhoCompras.IncluirNoCarrinho(Convert.ToInt16(rawId))
            Else
                Debug.Fail("ERROR : Falta o produtoId para chamar IncluirNoCarrinho.aspx")
                Throw New Exception("ERROR : Não se pode carregar IncluirNoCarrinho.aspx sem definir um ProdutoId.")
            End If
            Response.Redirect("Carrinho.aspx")
        End Sub

    End Class
End Namespace

Quando a página IncluirNoCarrinho.aspx for carregada um ID do produto é obtido a partir da query string de consulta. Em seguida, uma instância da classe carrinho de compras é criada e usada para chamar o método IncluirNoCarrinho.

Este método, contido no arquivo CarrinhoActions.vb, inclui a lógica para adicionar o produto selecionado no carrinho de compras ou aumentar a quantidade de produto selecionado.

Se o produto não tiver sido adicionado ao carrinho de compras, o produto é adicionado à tabela de CarrinhoItem do banco de dados.  Se o produto já tiver sido adicionado ao carrinho de compras e o usuário adicionar um item adicional do mesmo produto, a quantidade de produto é incrementada na tabelaCarrinhoItem. Finalmente, a página redireciona o usuário para a página Carrinho.aspx que vamos criar em seguida; nesta página o usuário vê uma lista atualizada dos itens no carrinho.

O ID de usuário é usado para identificar os produtos que estão associados a um determinado usuário e este ID é adicionado na tabela CarrinhoItem cada vez que o usuário adiciona um produto no carrinho de compras.

Criando a interface carrinho de compras

Vamos criar a página Carrinho.aspx, que exibirá os produtos que o usuário adicionou ao seu carrinho de compras e fornecer a capacidade de adicionar, remover e atualizar itens no carrinho de compras.

No menu Project clique em Add New Item e na janela Add New Item selecione o item Web Form using Master Page, informando o nome Carrinho.aspx;

aspn_4554

Selecione a master page Site.Master e clique em OK.

A seguir defina o código abaixo:

<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="Carrinho.aspx.vb" Inherits="WingTipToys.Carrinho" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
    <div id="CarrinhoTitulo" runat="server" class="ContentHead"><h1>Carrinho de Compras</h1></div>
    <asp:GridView ID="CarrinhoLista" runat="server" AutoGenerateColumns="False" ShowFooter="True" GridLines="Vertical" CellPadding="4"
        ItemType="WingtipToys.WingtipToys.Models.CarrinhoItem" CssClass="CarrinhoListaItem" SelectMethod="GetCarrinhoItens" Width="600">              
        <AlternatingRowStyle CssClass="CarrinhoListaItemAlt" />
        <Columns>
        <asp:BoundField DataField="ProdutoID" HeaderText="ID" SortExpression="ProduotID" />        
        <asp:BoundField DataField="Produto.ProdutoNome" HeaderText="Nome" SortExpression="ProdutoNome" />        
        <asp:BoundField DataField="Produto.PrecoUnitario" HeaderText="Preço (un.)" DataFormatString="{0:c}"/>     
        <asp:TemplateField   HeaderText="Quantidade">            
                <ItemTemplate>
                    <asp:TextBox ID="QuantidadeComprada" Width="40" runat="server" Text="<%#: Item.Quantidade%>"></asp:TextBox> 
                </ItemTemplate>        
        </asp:TemplateField>    
        <asp:TemplateField HeaderText="Total ">            
                <ItemTemplate>
                    <%#: String.Format("{0:c}", ((Convert.ToDouble(Item.Quantidade)) * Convert.ToDouble(Item.Produto.PrecoUnitario)))%>
                </ItemTemplate>        
        </asp:TemplateField> 
        <asp:TemplateField HeaderText="Remover Item">            
                <ItemTemplate>
                    <asp:CheckBox id="Remover" runat="server"></asp:CheckBox>
                </ItemTemplate>        
        </asp:TemplateField>    
        </Columns>    
        <FooterStyle CssClass="CarrinhoListaFooter"/>
        <HeaderStyle  CssClass="CarrinhoListaHead" />
    </asp:GridView>
    <div>
        <p></p>
        <strong>
            <asp:Label ID="LabelTotalText" runat="server" Text="Total Pedido: "></asp:Label>
            <asp:Label CssClass="NormalBold" id="lblTotal" runat="server" EnableViewState="false"></asp:Label>
        </strong> 
    </div>
    <br />
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
</asp:Content>

A página Carrinho.aspx inclui um controle GridView chamado CarrinhoLista. Este controle utiliza o model binding para vincular os dados do carrinho de compras do banco de dados com o controle GridView.

Quando você define a propriedade ItemType do controle GridView, a expressão de ligação de dados Item está disponível na marcação do controle e o controle torna-se fortemente tipado. Com isso você pode selecionar detalhes do objeto do item usando o IntelliSense.

Para configurar um controle de dados para usar o Model Binding para selecionar dados, defina a propriedade SelectMethod do controle. Na marcação acima, definimos o SelectMethodpara usar o método GetCarrinhotens que retorna uma lista de objetos CarrinhoItem.

O controle GridView chama o método no momento apropriado do ciclo de vida da página e vincula automaticamente os dados retornados. O método GetCarrinhotens ainda precisa ser criado.

Retornando os itens dos carrinho de compras

Nossa próxima tarefa é criar o código no code-behind da página Carrinho.aspx para recuperar e preencher os itens e exibi-los no carrinho de compras.

Na janela Solution Explorer clique com o botão direito do mouse sobre a página Carrinho.aspx e selecione View Code. A seguir, defina o código abaixo no code-behind:

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports WingTipToys.WingtipToys.Models
Imports WingTipToys.WingtipToys.Logic

Public Class Carrinho
     Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub

Public Function GetCarrinhoItens() As List(Of CarrinhoItem)
   Dim actions As New CarrinhoActions()
   Return actions.GetCarrinhoItens()
End Function

End Class

O controle GridView chama o método GetCarrinhotens no momento apropriado do ciclo de vida da página e vincula automaticamente os dados retornados. O método GetCarrinhotenscria uma instância do objeto CarrinhoActions e em seguida o código usa essa instância para retornar os itens no carrinho chamando o método GetCarrinhoItens.

Adicionando produtos ao carrinho

Quando a página ProdutoLista.aspx ou ProdutoDetalhes.aspx é exibida, o usuário será capaz de adicionar o produto ao carrinho de compras usando um link. Quando o usuário clicar no link, o aplicativo navega para a página de processamento chamada IncluirNoCarrinho.aspx. Esta página vai chamar o método IncluirNoCarrinho da classe Carrinho.

Vamos adicionar um link Incluir no carrinho em ambas as páginas sendo que este link incluirá o ID do produto que será retornado a partir do banco de dados.

Na janela Solution Explorer abra o arquivo ProdutoLista.aspx e copie o código abaixo para criar o link para incluir o produto pelo seu id:

<a href="/IncluirNoCarrinho.aspx?produtoID=<%#:Item.ProdutoID %>">               
        <span class="ProdutoListaItem">
            <b>Incluir no Carrinho<b>
        </span>           
</a>

Abaixo vemos um trecho do arquivo ProdutoLista.aspx com o código acima incluído:

aspn_4555

Agora abra o arquivo ProdutoDetalhes.aspx e inclua o mesmo de código para criar o link conforme mostra o trecho de código abaixo:

aspn_4556

1, 2, 3… Testando!

Vamos executar a aplicação e verificar se as funcionalidades adicionadas para a inclusão de produtos estão funcionando.

Clique com o botão direito do mouse sobre a página Default.aspx e selecione Set As Start Page. Pressione CTRL+F5 para rodar a aplicação e selecione Carros a partir do menu de navegação de categorias:

aspn_4557

A seguir, escolha um item e clique no link Incluir no Carrinho. A página carrinho.aspx irá exibir o item selecionado com opções para alterar a quantidade e uma checkbox para selecionar a remoção do item;

aspn_4558

Calculando o exibindo os valores dos itens no carrinho

Vamos agora definir um método chamado GetTotal na classe Carrinho para exibir a quantidade total do pedido.

Na janela Solution Explorer selecione o arquivo CarrinhoActions na pasta Logic, e inclua o método GetTotal na classe conforme o código a seguir:

   Public Function GetTotal() As Decimal
            CarrinhoId = GetCarrinhoId()
            ' Multiplica o preço pela quantidade do produto para obter
            ' o preço atual dos produtos no carrinho
            ' soma todos os totaisl para obter o total do carrinho
            Dim total As System.Nullable(Of Decimal) = Decimal.Zero

            total = CType((From carrinhoItens In _db.CarrinhoItens
                           Where carrinhoItens.CarrinhoId = CarrinhoId
                           Select CType(carrinhoItens.Quantidade, System.Nullable(Of Integer)) _
                           * carrinhoItens.Produto.PrecoUnitario).Sum(), System.Nullable(Of Decimal))

            Return If(total, Decimal.Zero)
        End Function

Primeiro, o método GetTotal obtém a identificação do carrinho de compras para o usuário. Em seguida, o método obtém o total do carrinho, multiplicando-se o preço do produto pela quantidade do produto para cada produto listado no carrinho.

Alterando a exibição no carrinho de compras

Vamos modificar o código da página Carrinho.aspx para chamar o método GetTotal e exibir esse total na página quando ela for carregada.

Selecione o arquivo Carrinho.aspx e clique com o botão direito do mouse sobre ele escolhendo a opção View Code. A seguir, inclua o código destacado em azul no evento Load da página abaixo no arquivo:

Imports System.Collections.Generic
Imports System.Linq
Imports WingTipToys.WingtipToys.Models
Imports WingTipToys.WingtipToys.Logic

Public Class Carrinho
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim usuarioCarrinhoCompra As New CarrinhoActions()
        Dim TotalCarrinho As Decimal = 0
        TotalCarrinho = usuarioCarrinhoCompra.GetTotal()
        If TotalCarrinho > 0 Then
            ' exibe o  Total.
            lblTotal.Text = [String].Format("{0:c}", TotalCarrinho)
        Else
            LabelTotalText.Text = ""
            lblTotal.Text = ""
            CarrinhoTitulo.InnerText = "O carrinho de compras esta vazio"
        End If
    End Sub
    Public Function GetCarrinhoItens() As List(Of CarrinhoItem)
        Dim actions As New CarrinhoActions()
        Return actions.GetCarrinhoItens()
    End Function
End Class

Quando a página Carrinho.aspx for carregada ela carrega o objeto carrinho de compras e, em seguida, recupera o total do carrinho de compras, chamando o método GetTotal da classe Carrinho. Se o carrinho de compras está vazio, uma mensagem será exibida informando o fato ao usuário.

1, 2, 3… Testando!

Vamos executar a aplicação e verificar se as funcionalidades adicionadas para inclusão de produtos e exibição dos totais estão funcionando.

Clique com o botão direito do mouse sobre a página Default.aspx e selecione Set As Start Page. Depois pressione CTRL+F5 para rodar a aplicação e selecione Carros, a partir do menu de navegação de categorias, e a seguir selecione um produto e clique no link - Incluir no Carrinho. Repita o procedimento selecionando outra categoria e outro produto de forma a ter mais de item no seu carrinho.

Abaixo vemos o resultado para a seleção de dois produtos distintos:

aspn_4559

Verificamos que o total é obtido corretamente a partir das quantidades e preços dos produtos no carrinho.

Incluindo os botões para atualizar e realizar o checkout no carrinho de compras

Para permitir que os usuários modifiquem o carrinho de compras, vamos adicionar um botão de atualização e um botão para fazer o Checkout na página do carrinho de compras.

Abra o arquivo Carrinho.aspx e selecione o modo Source de exibição. Para adicionar o botão Atualizar e o botão CheckOut na página Carrinho.aspx, adicione a marcação em amarelo para a marcação existente, como mostra o seguinte código:

aspn_455a

Quando o usuário clicar no botão Atualizar, o manipulador de eventos btnAtualizar_Click será chamado. Este manipulador de eventos irá chamar o código que vamos adicionar logo a seguir.

Em seguida, você pode atualizar o código contido no arquivo Carrinho.aspx.vb para percorrer os itens do carrinho e chamar os métodos RemoveItem e UpdateItem.

Abra o arquivo code-behind Carrinho.aspx.vb e inclua o código destacado em negrito:

Imports System.Collections.Generic
Imports System.Linq
Imports WingTipToys.WingtipToys.Models
Imports WingTipToys.WingtipToys.Logic
<strong>Imports System.Collections.Specialized
Imports System.Collections
Imports System.Web.ModelBinding</strong>

Public Class Carrinho
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim usuarioCarrinhoCompra As New CarrinhoActions()
        Dim TotalCarrinho As Decimal = 0
        TotalCarrinho = usuarioCarrinhoCompra.GetTotal()
        If TotalCarrinho &gt; 0 Then
            ' exibe o Total.
            lblTotal.Text = [String].Format("{0:c}", TotalCarrinho)
        Else
            LabelTotalText.Text = ""
            lblTotal.Text = ""
            CarrinhoTitulo.InnerText = "O carrinho de compras esta vazio"
            btnAtualizar.Visible = False
        End If
    End Sub
    Public Function GetCarrinhoItens() As List(Of CarrinhoItem)
        Dim actions As New CarrinhoActions()
        Return actions.GetCarrinhoItens()
    End Function

    <strong>Public Function AtualizaItensCarrinho() As List(Of CarrinhoItem)
        Dim usuariosCarrinho As New CarrinhoActions()
        Dim carrinhoId As String = usuariosCarrinho.GetCarrinhoId()

        Dim carrinhoAtualiza As CarrinhoActions.CarrinhoAtualiza() = New CarrinhoActions.CarrinhoAtualiza(CarrinhoLista.Rows.Count - 1) {}

        For i As Integer = 0 To CarrinhoLista.Rows.Count - 1
            Dim rowValues As IOrderedDictionary = New OrderedDictionary()
            rowValues = GetValues(CarrinhoLista.Rows(i))
            carrinhoAtualiza(i).ProdutoId = Convert.ToInt32(rowValues("ProdutoID"))

            Dim cbRemove As New CheckBox()
            cbRemove = DirectCast(CarrinhoLista.Rows(i).FindControl("Remove"), CheckBox)
            carrinhoAtualiza(i).RemoveItem = cbRemove.Checked

            Dim quantidadeTextBox As New TextBox()
            quantidadeTextBox = DirectCast(CarrinhoLista.Rows(i).FindControl("QuantidadeComprada"), TextBox)
            carrinhoAtualiza(i).QuantidadeComprada = Convert.ToInt16(quantidadeTextBox.Text.ToString())
        Next
        usuariosCarrinho.AtualizaCarrinhoBD(carrinhoId, carrinhoAtualiza)
        CarrinhoLista.DataBind()
        lblTotal.Text = [String].Format("{0:c}", usuariosCarrinho.GetTotal())
        Return usuariosCarrinho.GetCarrinhoItens()
    End Function

    Public Shared Function GetValues(row As GridViewRow) As IOrderedDictionary
        Dim values As IOrderedDictionary = New OrderedDictionary()
        For Each cell As DataControlFieldCell In row.Cells
            If cell.Visible Then
                ' Extrai os valores da celula
                cell.ContainingField.ExtractValuesFromCell(values, cell, row.RowState, True)
            End If
        Next
        Return values
    End Function

    Protected Sub btnAtualizar_Click(sender As Object, e As EventArgs) Handles btnAtualizar.Click
        AtualizaItensCarrinho()
    End Sub</strong>
End Class

Quando o usuário clica no botão Atualizar, na página Carrinho.aspx, o método AtualizaItensCarrinho() é chamado. Este método recebe os valores atualizados para cada item no carrinho de compras. Em seguida, o método AtualizaCarrinhoBD (que será criado mais adiante) utilizado para adicionar ou remover itens do carrinho de compras.

Uma vez que o banco de dados foi atualizado para refletir as atualizações do carrinho de compras, o controle GridView é atualizado na página do carrinho, chamando o método DataBind para o GridView. Além disso, o valor total do pedido na página do carrinho de compras é atualizado para refletir a lista atualizada dos itens.

Atualizando e removendo itens do carrinho de compras

Na página Carrinho.aspx, podemos ver que os controles foram adicionados para atualizar a quantidade de um item e remover um item. Agora, vamos adicionar o código que vai fazer esses controles funcionarem.

Abra o arquivo CarrinhoAction.vb na pasta Logic e inclua o código destacado em azul neste arquivo:

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports WingTipToys.WingtipToys.Models

Namespace WingtipToys.Logic

    Public Class CarrinhoActions
        Public Property CarrinhoId() As String
            Get
                Return m_CarrinhoId
            End Get
            Set(value As String)
                m_CarrinhoId = value
            End Set
        End Property
        Private m_CarrinhoId As String

        Private _db As New ProdutoContexto()

        Public Const CarrinhoSessaoChave As String = "CarrinhoId"

        Public Sub IncluirNoCarrinho(id As Integer)
            ' Retorna o produto do banco de dados
            CarrinhoId = GetCarrinhoId()

            Dim carrinhoItem = _db.CarrinhoItens.SingleOrDefault(Function(c) c.CarrinhoId = CarrinhoId AndAlso c.ProdutoId = id)
            If carrinhoItem Is Nothing Then
                ' Cria um novo item no carrinho se não existir
                carrinhoItem = New CarrinhoItem() With { _
                    .ItemId = Guid.NewGuid().ToString(), _
                    .ProdutoId = id, _
                    .CarrinhoId = CarrinhoId, _
                    .Produto = _db.Produtos.SingleOrDefault(Function(p) p.ProdutoID = id), _
                    .Quantidade = 1, _
                    .DataCriacao = DateTime.Now _
                }

                _db.CarrinhoItens.Add(carrinhoItem)
            Else
                ' se o item não existe acrescenta um unidade
                carrinhoItem.Quantidade += 1
            End If
            _db.SaveChanges()
        End Sub

        Public Function GetCarrinhoId() As String
            If HttpContext.Current.Session(CarrinhoSessaoChave) Is Nothing Then
                If Not String.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name) Then
                    HttpContext.Current.Session(CarrinhoSessaoChave) = HttpContext.Current.User.Identity.Name
                Else
                    ' Gera um novo GUID aleatorio usado usando a classe System.Guid
                    Dim tempCartId As Guid = Guid.NewGuid()
                    HttpContext.Current.Session(CarrinhoSessaoChave) = tempCartId.ToString()
                End If
            End If
            Return HttpContext.Current.Session(CarrinhoSessaoChave).ToString()
        End Function

        Public Function GetCarrinhoItens() As List(Of CarrinhoItem)
            CarrinhoId = GetCarrinhoId()
            Return _db.CarrinhoItens.Where(Function(c) c.CarrinhoId = CarrinhoId).ToList()
        End Function

        Public Function GetTotal() As Decimal
            CarrinhoId = GetCarrinhoId()
            ' Multiplica o preço pela quantidade do produto para obter
            ' o preço atual dos produtos no carrinho
            ' soma todos os totaisl para obter o total do carrinho
            Dim total As System.Nullable(Of Decimal) = Decimal.Zero

            total = CType((From carrinhoItens In _db.CarrinhoItens
                           Where carrinhoItens.CarrinhoId = CarrinhoId
                           Select CType(carrinhoItens.Quantidade, System.Nullable(Of Integer)) _
                           * carrinhoItens.Produto.PrecoUnitario).Sum(), System.Nullable(Of Decimal))

            Return If(total, Decimal.Zero)
        End Function

        Public Function GetCarrinho(context As HttpContext) As CarrinhoActions
            Dim carrinho = New CarrinhoActions()
            carrinho.CarrinhoId = carrinho.GetCarrinhoId()
            Return carrinho
        End Function

        Public Sub AtualizaCarrinhoBD(carrinhoId As String, CarrinhoItemAtualiza As CarrinhoAtualiza())
            Using db = New ProdutoContexto()
                Try
                    Dim CartItemCount As Integer = CarrinhoItemAtualiza.Count()
                    Dim myCart As List(Of CarrinhoItem) = GetCarrinhoItens()
                    For Each carrinhoItem In myCart
                        ' Itera através de todas aslinhas na lista de carrinho
                        For i As Integer = 0 To CartItemCount - 1
                            If carrinhoItem.Produto.ProdutoID = CarrinhoItemAtualiza(i).ProdutoId Then
                                If CarrinhoItemAtualiza(i).QuantidadeComprada < 1 OrElse CarrinhoItemAtualiza(i).RemoveItem = True Then
                                    RemoveItem(carrinhoId, carrinhoItem.ProdutoId)
                                Else
                                    UpdateItem(carrinhoId, carrinhoItem.ProdutoId, CarrinhoItemAtualiza(i).QuantidadeComprada)
                                End If
                            End If
                        Next
                    Next
                Catch exp As Exception
                    Throw New Exception("ERRO: Não foi possível atualizar o Banco de dados - " + exp.Message.ToString(), exp)
                End Try
            End Using
        End Sub

        Public Sub RemoveItem(removeCarrinhoID As String, removeProdutoID As Integer)
            Using db = New ProdutoContexto()
                Try
                    Dim meuItem = (From c In db.CarrinhoItens
                                           Where c.CarrinhoId = removeCarrinhoID _
                                           AndAlso c.Produto.ProdutoID = removeProdutoID).FirstOrDefault()

                    If meuItem IsNot Nothing Then
                        ' db.DeleteObject(meuItem);
                        db.CarrinhoItens.Remove(meuItem)
                        db.SaveChanges()
                    End If
                Catch exp As Exception
                    Throw New Exception("ERROR: Não foi possível Remover o Item do Carrinho - " + exp.Message.ToString(), exp)
                End Try
            End Using
        End Sub

        Public Sub UpdateItem(atualizaCarrinhoID As String, atualizaProdutoID As Integer, quantidade As Integer)
            Using db = New ProdutoContexto()
                Try
                    Dim meuItem = (From c In db.CarrinhoItens Where c.CarrinhoId = atualizaCarrinhoID AndAlso c.Produto.ProdutoID = atualizaProdutoID).FirstOrDefault()
                    If meuItem IsNot Nothing Then
                        meuItem.Quantidade = quantidade
                        db.SaveChanges()
                    End If
                Catch exp As Exception
                    Throw New Exception("ERROR: Não foi possível atualizar o item do carrinho - " & exp.Message.ToString(), exp)
                End Try
            End Using
        End Sub

        Public Sub CarrinhoVazio()
            CarrinhoId = GetCarrinhoId()
            Dim carrinhoItens = _db.CarrinhoItens.Where(Function(c) c.CarrinhoId = CarrinhoId)
            For Each carrinhoItem In carrinhoItens
                _db.CarrinhoItens.Remove(carrinhoItem)
            Next
            ' Salva as mudanças
            _db.SaveChanges()
        End Sub

        Public Function GetContador() As Integer
            CarrinhoId = GetCarrinhoId()
            ' Obtem a contagem de cada item no carrinho e as soma
            Dim contador As System.Nullable(Of Integer) = (From carrinhoItems In _db.CarrinhoItens
                                                                                     Where carrinhoItems.CarrinhoId = CarrinhoId
                                                                                      Select CType(carrinhoItems.Quantidade, System.Nullable(Of Integer))).Sum()
            ' Retorna 0 se todas as entradas forem nulas
            Return If(contador, 0)
        End Function

        Public Structure CarrinhoAtualiza
            Public ProdutoId As Integer
            Public QuantidadeComprada As Integer
            Public RemoveItem As Boolean
        End Structure

    End Class
End Namespace

O método AtualizaCarrinhoBD, chamado a partir do método AtualizaItensCarrinho() na página Carrinho.aspx.vb, contém a lógica para atualizar ou remover itens do carrinho de compras. O método AtualizaCarrinhoBD percorre todas as linhas dentro da lista carrinho de compras.

Se um item de carrinho de compras foi marcado para ser removido, ou a quantidade for menor do que um, o método RemoveItem é chamado. Caso contrário, o item do carrinho de compras está marcado para atualizações e o método UpdateItem é chamado. Depois que o item do carrinho de compras foi removido ou atualizado, as alterações dos dados são salvas.

A estrutura CarrinhoAtualiza é usada para armazenar todos os itens do carrinho de compras. O método AtualizaCarrinhoBD usa a estrutura CarrinhoAtualiza para determinar se qualquer um dos elementos precisa de ser atualizado ou removido.

No próximo artigo, vamos usar o método CarrinhoVazio para limpar o carrinho de compras após a compra de produtos. No momento vamos usar o método GetContador que acabamos de incluir para determinar quantos itens estão no carrinho de compras.

Incluindo um contador no carrinho de compras

Para permitir que o usuário veja o número total de itens no carrinho de compras, vamos adicionar um contador na página Site.Master. Este contador também vai atuar como um link para o carrinho de compras.

Na janela Solution Explorer abra o arquivo Site.Master e faça as alterações conforme o código destacado em abaixo no trecho de código deste arquivo:

aspn_455b

A seguir, vamos atualizar o code-behind Site.Master.vb com o código abaixo:

aspn_455c

Antes da que a página seja processada no navegador, o evento Page_PreRender é gerado.

No manipulador Page_PreRender, a contagem total do carrinho de compras é determinada chamando o método GetContador. O valor retornado é adicionado à extensãocarrinhoContador incluído na marcação da página Site.Master.

As tags <span> permitem que os elementos internos sejam devidamente renderizados. Quando qualquer página do site é exibida, o total do carrinho de compras será mostrado.

O usuário também poderá clicar no total do carrinho de compras para visualizar o carrinho de compras.

1, 2, 3… Testando e concluindo!

Vamos executar o aplicativo agora para ver como podemos adicionar, excluir e atualizar os itens no carrinho de compras. O total carrinho de compras irá refletir o custo total de todos os itens no carrinho de compras.

  • Pressione Ctrl + F5 para executar o aplicativo;
  • O navegador abre e mostra a página Default.aspx;
  • Selecione Carros no menu de navegação categoria;
  • Clique no link Adicionar no carrinho de compras ao lado do primeiro produto;
  • A página Carrinho.aspx é exibida com o total do pedido;
  • Selecione Aeronaves do menu de navegação categoria;
  • Clique no link Adicionar no carrinho de compras ao lado do primeiro produto;
  • Defina a quantidade do primeiro item no carrinho de compras para 3 e marque a caixa de seleção Remover item do segundo item;

aspn_455d

  • Clique no botão Atualizar para atualizar a página do carrinho de compras e exibir o total nova ordem.

aspn_455e

Após concluir todas as implementações discutidas aqui, nosso carrinho de compras suporta a adição, exclusão e atualização de itens que o usuário tenha selecionado para compra. Além de implementar a funcionalidade de carrinho de compras, você aprendeu como exibir itens de carrinho de compras em um controle GridView e calcular o total do pedido.

Ufa! Acabamos…

O post ASP .NET 4.5 Web Forms – Incluindo o carrinho de compras – Parte 05 apareceu primeiro em .

ASP .NET 4.5 Web Forms – Autorizando e registrando o usuário – Parte 05

$
0
0

Este artigo descreve como modificar o site criado nos artigos anteriores para incluir a autorização do usuário e o registro (a implementação do pagamento através de transferências bancárias fica por sua conta).

Apenas usuários que estão conectados terão autorização para comprar produtos. O modelo de projeto Web Forms da ASP.NET 4.5 já possui a funcionalidade interna de registro de usuário e já inclui muitso dos recursos que você precisa.

No final do artigo, você irá testar o aplicativo selecionando produtos para adicionar ao carrinho de compras, clicar no botão check-out.

Observações:

  1. A transferência dos dados para o site de testes do PayPal, confirmação dos dados de entrega e informações de pagamento com  retorno ao site para confirmar e completar a compra ficará por sua conta.
  2. A implantação da funcionalidade PayPal não será tratada.

Existem vários processadores de pagamento de terceiros que se especializam em compras on-line com escalabilidade e segurança. Como desenvolvedor ASP.NET, você deve considerar as vantagens de se utilizar uma solução de pagamento de terceiros antes de implementar uma solução de compras on-line.

O que você vai aprender:

  • Como restringir o acesso a páginas específicas em uma pasta;
  • Como criar um carrinho de compras conhecido a partir de um carrinho de compras anônimo.

Este artigo foi integralmente baseado no artigo original: http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/checkout-and-payment-with-paypal (com algumas alterações e portado para a linguagem VB .NET)

Incluindo o rastreamento do pedido

Neste artigo, você vai criar duas novas classes para rastrear os dados da ordem de criadas por um usuário. As classes irão rastrear os dados sobre informações de entrega, total, compra e confirmação de pagamento.

Em artigos anteriores nós já definimos o esquema para categorias, produtos e itens de carrinho de compras, criando as classes Categoria, Produto e CarrinhoItem na pasta Models. Agora vamos adicionar duas novas classes para definir o esquema para o pedido do produto e os detalhes do pedido.

Clique com o botão direito do mouse sobre a pasta Models e selecione Add -> New Item. Na janela aberta, selecione o item Code e a seguir selecione Class. Informe o nome Pedido.vb e clique no botão Add. Substitua o código deste arquivo pelo código abaixo:

Imports System.ComponentModel.DataAnnotations
Imports System.Collections.Generic
Imports System.ComponentModel

Public Class Pedido
    Public Property PedidoId() As Integer
        Get
            Return m_PedidoId
        End Get
        Set(value As Integer)
            m_PedidoId = value
        End Set
    End Property
    Private m_PedidoId As Integer

    Public Property PedidoData() As System.DateTime
        Get
            Return m_PedidoData
        End Get
        Set(value As System.DateTime)
            m_PedidoData = value
        End Set
    End Property
    Private m_PedidoData As System.DateTime

    Public Property NomeUsuario() As String
        Get
            Return m_NomeUsuario
        End Get
        Set(value As String)
            m_NomeUsuario = value
        End Set
    End Property
    Private m_NomeUsuario As String

    <Required(ErrorMessage:="O nome é obrigatório")> _
    <DisplayName("Nome")> _
    <StringLength(160)> _
    Public Property Nome() As String
        Get
            Return m_Nome
        End Get
        Set(value As String)
            m_Nome = value
        End Set
    End Property
    Private m_Nome As String

    <Required(ErrorMessage:="O Sobrenome é obrigatório")> _
    <DisplayName("Sobrenome")> _
    <StringLength(160)> _
    Public Property Sobrenome() As String
        Get
            Return m_Sobrenome
        End Get
        Set(value As String)
            m_Sobrenome = value
        End Set
    End Property
    Private m_Sobrenome As String

    <Required(ErrorMessage:="O Endereço é obrigatório")> _
    <StringLength(70)> _
    Public Property Endereco() As String
        Get
            Return m_Endereco
        End Get
        Set(value As String)
            m_Endereco = value
        End Set
    End Property
    Private m_Endereco As String

    <Required(ErrorMessage:="A cidade é obrigatória")> _
    <StringLength(40)> _
    Public Property Cidade() As String
        Get
            Return m_Cidade
        End Get
        Set(value As String)
            m_Cidade = value
        End Set
    End Property
    Private m_Cidade As String

    <Required(ErrorMessage:="O Estado é obrigatório.")> _
    <StringLength(40)> _
    Public Property Estado() As String
        Get
            Return m_Estado
        End Get
        Set(value As String)
            m_Estado = value
        End Set
    End Property
    Private m_Estado As String

    <Required(ErrorMessage:="O código Postal é obrigatório")> _
    <DisplayName("Código Postal")> _
    <StringLength(10)> _
    Public Property CodigoPostal() As String
        Get
            Return m_CodigoPostal
        End Get
        Set(value As String)
            m_CodigoPostal = value
        End Set
    End Property
    Private m_CodigoPostal As String

    <Required(ErrorMessage:="O País é obrigatório")> _
    <StringLength(40)> _
    Public Property Pais() As String
        Get
            Return m_Pais
        End Get
        Set(value As String)
            m_Pais = value
        End Set
    End Property
    Private m_Pais As String

    <StringLength(24)> _
    Public Property Telefone() As String
        Get
            Return m_Telefone
        End Get
        Set(value As String)
            m_Telefone = value
        End Set
    End Property
    Private m_Telefone As String

    <Required(ErrorMessage:="O Email é obrigatório")> _
    <DisplayName("Email")> _
    <RegularExpression("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}", ErrorMessage:="Email inválido.")> _
    <DataType(DataType.EmailAddress)> _
    Public Property Email() As String
        Get
            Return m_Email
        End Get
        Set(value As String)
            m_Email = value
        End Set
    End Property
    Private m_Email As String

    <ScaffoldColumn(False)> _
    Public Property Total() As Decimal
        Get
            Return m_Total
        End Get
        Set(value As Decimal)
            m_Total = Value
        End Set
    End Property
    Private m_Total As Decimal

    <ScaffoldColumn(False)> _
    Public Property PagamentoTransacaoId() As String
        Get
            Return m_PagamentoTransacaoId
        End Get
        Set(value As String)
            m_PagamentoTransacaoId = value
        End Set
    End Property
    Private m_PagamentoTransacaoId As String

    <ScaffoldColumn(False)> _
    Public Property FoiEnviado() As Boolean
        Get
            Return m_FoiEnviado
        End Get
        Set(value As Boolean)
            m_FoiEnviado = value
        End Set
    End Property
    Private m_FoiEnviado As Boolean

    Public Property PedidoDetalhes() As List(Of PedidoDetalhe)
        Get
            Return m_PedidoDetalhes
        End Get
        Set(value As List(Of PedidoDetalhe))
            m_PedidoDetalhes = value
        End Set
    End Property
    Private m_PedidoDetalhes As List(Of PedidoDetalhe)
End Class

Da mesma forma, vamos incluir uma nova classe chamada PedidoDetalhe na pasta Models com o seguinte código:

Imports System.ComponentModel.DataAnnotations

Public Class PedidoDetalhe

    'usando a nova notação das propriedades automáticas
    Public Property PedidoDetalheId As Integer
    Public Property PedidoId As Integer
    Public Property NomeUsuario As String
    Public Property ProdutoId As Integer
    Public Property Quantidade As Integer

    'usando a notação antiga para definir uma propriedade
    Public Property PrecoUnitario() As System.Nullable(Of Double)
        Get
            Return m_PrecoUnitario
        End Get
        Set(value As System.Nullable(Of Double))
            m_PrecoUnitario = value
        End Set
    End Property
    Private m_PrecoUnitario As System.Nullable(Of Double)

End Class

As classes Pedido e PedidoDetalhe contêm o esquema para definir as informações do pedido usado para a compra e transporte.

Além disso, vamos precisar atualizar a classe de contexto de banco de dados que gerencia as classes de entidade e que fornece acesso a dados para o banco de dados. Vamos incluir as classes Pedido e PedidoDetalhe na classe ProdutoContexto.

Abra o arquivo ProdutoContexto.vb e altere o seu código, incluindo as duas linhas de código destacadas em negrito que incluem a definição para a classe Pedido e PedidoDetalhe:

Imports System.Data.Entity

Namespace WingtipToys.Models

    Public Class ProdutoContexto
        Inherits DbContext

        Public Sub New()
            MyBase.New("WingtipToys")
        End Sub

        Public Property Categorias() As DbSet(Of Categoria)
        Public Property Produtos() As DbSet(Of Produto)
        Public Property CarrinhoItens() As DbSet(Of CarrinhoItem)
        <strong>Public Property Pedido As DbSet(Of Pedido)
        Public Property PedidoDetalhe As DbSet(Of PedidoDetalhe)</strong>

    End Class
End Namespace

O código no arquivo ProdutoContexto.vb adiciona o namespace System.Data.Entity para que você tenha acesso a todo o núcleo de funcionalidade do Entity Framework. Esta funcionalidade inclui a capacidade de consultar, inserir, atualizar e excluir dados, trabalhando com objetos fortemente tipados. O código acima adiciona acesso ao Entity Framework para as classes Pedido e PedidoDetalhe.

Adicionando acesso ao Checkout

A nossa aplicação permite que usuários anônimos escolham e adicionem produtos a um carrinho de compras. No entanto, quando os usuários anônimos optarem por comprar os produtos adicionados ao carrinho de compras, eles devem fazer logon no site. Depois de terem se registrado, eles podem acessar as páginas restritas do aplicação que lidam com os processo de checkout e compra. Estas páginas restritas estão contidas na pasta de checkout da aplicação.

Clique com o botão direito do mouse sobre o nome do projeto e selecione Add -> New Folder. A seguir, informe o nome Checkout para esta nova pasta do projeto. Depois, clique com o botão direito do mouse sobre a pasta Checkout e selecione Add New Item.

Na janela Add New Item selecione Visual Basic -> Web e escolha o template Web Form using Master Page, informando o nome InicioCheckout.aspx e clicando no botão Add.

aspn_4561

Selecione a master page Site.Master e clique em OK.

Da mesma forma, repetindo os procedimentos acima, vamos incluir na pasta Checkout as seguintes páginas (todas elas usando o template Web Form using Master Page):

  • ReverCheckout.aspx;
  • CompletarCheckout.aspx;
  • CancelarChechout.aspx;
  • ErroCheckout.aspx.

Obs: Estou mostrando como criar as páginas, mas não vou implementá-las neste artigo; ficando isso por sua conta.

Vamos agora incluir na pasta Checkout um arquivo Web.Config para restringir o acesso a todas as páginas contidas nesta pasta.

Clique com o botão direito do mouse sobre a pasta Checkout e selecione Add -> New Item. A seguir, selecione Visual Basic -> Web e o template Web Configuration File, aceitando o nome Web.Config e clicando no botão Add.

aspn_4562

Substitua o código deste arquivo pelo seguinte código XML:

<?xml version="1.0"?>
<configuration>
    <system.web>
      <authorization>
        <deny users="?"/>        
      </authorization>
    </system.web>
</configuration>

O arquivo Web.config especifica que todos os usuários desconhecidos da aplicação devem ter o acesso negado às páginas contidas na pasta Checkout. No entanto, se o usuário possuir uma conta registrada e está logado, ele passa a ser um usuário conhecido e terá acesso às páginas na pasta Checkout.

Permitindo o login a partir de outros site com OpenId e OAuth

A ASP.NET Web Forms oferece opções avançadas para a adesão e autenticação. Essas melhorias incluem os novos providers OAuth e OpenID. Usando esses provedores, você pode permitir que os usuários façam o login em seu site utilizando as suas credenciais existentes no Facebook, Twitter, Windows Live e Google.

Por exemplo, para fazer login usando uma conta no Facebook, os usuários podem escolher apenas uma opção para o Facebook, que redireciona para a página de login do Facebook, onde ele insere suas credenciais de usuário.

Eles podem, então, associar o login com a sua conta do Facebook em seu site. Um aprimoramento relacionado ao recurso membership, da Web Forms ASP.NET, é que os usuários podem associar vários logins (incluindo logins de redes sociais), com uma única conta em seu site.

Quando você adiciona um provedor de OAuth (Facebook, Twitter ou Windows Live) para o seu aplicativo ASP.NET Web Forms, você deve definir o valor do ID do aplicativo e um valor secreto da aplicação. Você pode adicionar esses valores no arquivo AuthConfig.vb em sua aplicação Web Forms. Além disso, você deve criar um aplicativo no site externo (Facebook, Twitter ou Windows Live). Ao criar o aplicativo no site externo você pode obter as chaves de aplicativos que você precisa, a fim de chamar o recurso de login por esses sites.

Para sites que usam um provedor de OpenID (Google), você não precisa criar um aplicativo no site externo.

Na janela Solution Explorer, localize e abra a pasta App_Start. Abra o arquivo AuthConfig.vb e descomente a última linha de código que permite usar o OpenID para uma conta do Google:

aspn_4563

Agora, quando você executar a aplicação, você terá a opção de acessar sua conta Google e associar à conta o site a com sua conta Google.

Alterando a funcionalidade de login

Grande parte da funcionalidade de registro do usuário foi incluído no modelo ASP.NET Web Forms por padrão. Agora vamos modificar as páginas Login.aspx e Register.aspx para chamar o método MigrateCart.

O método MigrateCart associa um novo usuário conectado com um carrinho de compras anônimo. Ao associar o usuário eo  carrinho de compras, nossa aplicação será capaz de manter o carrinho de compras do usuário entre as visitas.

Na janela Solution Explorer abra a pasta Account e abra a página Login.aspx. A seguir, altere o controle Login, conforme a linha de código a seguir:

<asp:Login runat="server" ViewStateMode="Disabled" RenderOuterTable="false" ID="LoginCtrl" OnLoggedIn="LoginCtrl_LoggedIn">

Modifique também o arquivo code-behind Login.aspx.vb, incluindo o código destacado em negrito:

Public Class Login
    Inherits Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        RegisterHyperLink.NavigateUrl = "Register.aspx"
        OpenAuthLogin.ReturnUrl = Request.QueryString("ReturnUrl")

        Dim returnUrl = HttpUtility.UrlEncode(Request.QueryString("ReturnUrl"))
        If Not String.IsNullOrEmpty(returnUrl) Then
            RegisterHyperLink.NavigateUrl &amp;= "?ReturnUrl=" &amp; returnUrl
        End If
    End Sub

    <strong>Protected Sub LoginCtrl_LoggedIn(sender As Object, e As EventArgs)
        Dim usuarioCarrinhoCompras As New WingtipToys.Logic.CarrinhoActions()
        Dim carrinhoId As String = usuarioCarrinhoCompras.GetCarrinhoId()
        usuarioCarrinhoCompras.MigrateCart(carrinhoId, User.Identity.Name.ToString())</strong>
    End Sub
End Class

Por enquanto, vamos ignorar o alerta de que não há uma definição para o método MigrateCart. Vamos definir este método mais adiante.

O controle Login suporta um manipulador OnLoggedIn. Você modificou a marcação do controle Login na página Login.aspx, adicionando o atributo OnLoggedIn. Quando um usuário tiver terminado de fazer o logon no site usando o controle Login, o manipulador de eventos no arquivo Login.aspx.vb, que aponta para OnLoggedIn, dispara o evento LoginCtrl_LoggedIn.

Quando o manipulador de eventos LoginCtrl_LoggedIn que você adicionou ao Login.aspx.vb é chamado, uma nova instância do carrinho de compras usuariosCarrinhoCompras será criado.

A identificação do carrinho de compras (um GUID) é recuperada e definida para a variável carrinhoId. Em seguida, o método MigrateCart é chamado, passando tanto o carrinhoId e o nome do usuário conectado ao método. Quando o carrinho de compras é migrado, o GUID usado para identificar o carrinho de compras anônimo é substituído pelo nome do usuário.

Além de modificar a página Login.aspx para migrar o carrinho de compras, quando o usuário faz login, vamos modificar também a página Register.aspx para migrar o carrinho de compras quando o usuário cria uma nova conta e faz o login.

Na pasta Account, abra o arquivo code-behind Register.aspx.vb. Modifique o código deste arquivo incluindo o código destacado em azul a seguir:

aspn_4564

Ignore o alerta sobre o método MigrateCart e salve o arquivo com as alterações.

Observe que você usou o mesmo código no manipulador de eventos RegisterUser_CreatedUser, que você usou no manipulador de eventos LoginCtrl_LoggedIn. Quando o usuário se registrar ou fizer o logon, uma chamada para o método MigrateCart será feita.

Migrando o carrinho de compras

Agora que temos os processo de registro e login atualizados, vamos adicionar o código para migrar o carrinho de compras implementando o método MigrateCart.

Abra o arquivo CarrinhoActions.vb da pasta Logic na janela Solution Explorer e inclua o método MigrateCart conforme o código abaixo:

Public Sub MigrateCart(carrinho_Id As String, userName As String)
            Dim carrinhoCompras = _db.CarrinhoItens.Where(Function(c) c.CarrinhoId = carrinho_Id)
            For Each item As CarrinhoItem In carrinhoCompras
                item.CarrinhoId = userName
            Next
            HttpContext.Current.Session(CarrinhoSessaoChave) = userName
            _db.SaveChanges()
        End Sub

O método usa o MigrateCart e usa o carrinhoId existente para encontrar o carrinho de compras do usuário. Em seguida, o código percorre todos os itens do carrinho de compras e substitui a propriedade CarrinhoId (conforme especificado pelo esquema CarrinhoItem) com o registrado em nome do usuário.

Fazendo a integração com o PayPal (introdução)

Vou dar apenas a introdução na integração do PayPal, sendo que a implementação de todas as classes deverá ficar por sua conta (veja o artigo original).

O PayPal é uma plataforma de faturamento baseado na web que aceita pagamentos por comerciantes online. A seguir, vamos explicar como fazer a integração com o PayPal’s Express Checkout na nossa aplicação. O Express Checkout permite que seus clientes usem PayPal para pagar os itens que foram adicionados ao seu carrinho de compras.

Criando uma conta de testes no PayPal

Para utilizar o ambiente de testes do PayPal, você deve criar uma conta de teste do desenvolvedor. Você vai usar a conta de teste do desenvolvedor para criar uma conta de teste do comprador e uma conta de teste do vendedor. As credenciais da conta de testes do desenvolvedor também permitirá que nossa aplicação exemplo acesse o ambiente de testes do PayPal.

Abra o seu navegador e acesse site: https://developer.paypal.com

Se você não possuir uma conta de desenvolvedor, clique no botão: Sign Up Now e siga as instruções.

aspn_4565

Após criar sua conta, faça o login no site do PayPal para acessar o ambiente de testes.

Para criar uma conta de teste do comprador, procure no Titulo Test Accounts e clique em preconfigured para criar uma conta pre-configurada. Escolha um e-mail fictício para a conta de teste do comprador e uma senha. Note que a conta de e-mail do comprador que você escolher terá uma sequência gerada pelo PayPal adicionada ao nome da conta.

Clique em Create Account para criar uma conta de teste (buyer). Clique em preconfigured para criar uma conta de teste para vendedor (seller).

Para criar uma conta de teste do vendedor escolha o tipo de conta do vendedor(Seller). Escolha uma conta de login e senha fictícios. A conta de e-mail vendedor que você escolher irá também ter uma sequência gerada pelo PayPal adicionada ao nome da conta:

aspn_4566

Ao terminar o processo clique na opção Test Accounts do menu SandBox e na coluna Payment Review clique no link Disable para habilitar cada conta:

aspn_4567

Após isso clique na opção Home do Menu e, a seguir, clique na opção API and Payament Card Credentials para visualizar as suas credenciais.

Você vai precisar das credenciais apresentadas (API Username, API Password e Signature) para fazer chamadas da API na nossa aplicação para o ambiente de testes do PayPal.

Adicionar uma classe PayPal e credenciais da API (apenas a dica de como criar a classe)

Vamos colocar a maior parte do código do PayPal em uma única classe. Essa classe contém os métodos usados para se comunicar com PayPal. Além disso, vamos adicionar as credenciais PayPal a esta classe.

Clique com o botão direito do mouse sobre a pasta Logic e selecione Add -> New Item.

Selecione Visual Basic->Code e o template Class; informe o nome PayPalFunctions.vb e clique no botão Add;

aspn_4568

Agora, basta você copiar e colar o código exibido no artigo original nesta classe.

Obs: Toda a funcionalidade de implementação do PayPal está descrita no artigo original e que por questão de brevidade eu não vou tratar neste artigo.

Executando nossa aplicação e clicando no link Login iremos obter a seguinte página. Nela temos a opção de fazer o login usando a conta local ou a conta do Google:

Nota: Se você estiver sob um proxy poderá ocorrer um erro por falta de credenciais para passar pelo proxy.

aspn_4569

Se você usar a conta do Google será para redirecionar para uma página solicitando sua permissão:

aspn_456d

Se você permitir, será autenticado com as credenciais do Google bastando apenas confirmar clicando no botão Login:

aspn_456E

Você será redirecionado para a página Default.aspx onde poderá verificar o usuário que esta logado:

aspn_456f

A página referente ao link Registrar é mostrada a seguir:

aspn_456a

Para registrar um novo usuário basta preencher o formulário e clicar em Registrar:

aspn_456b

O novo usuário estará identificado e pronto para navegar e usar as funcionalidades do nosso site:

aspn_456c

No próximo artigo vamos incluir um perfil de administrador em nosso site e implementar o membership.

O post ASP .NET 4.5 Web Forms – Autorizando e registrando o usuário – Parte 05 apareceu primeiro em .

ASP .NET 4.5 Web Forms – Criando a área de Administração – Parte 07

$
0
0

Neste artigo, vamos atualizar a nossa aplicação ASP .NET Web Forms para adicionar uma função de administrador e usar associação, usando o recurso membership da ASP.NET. Ele também mostra como implementar uma página de administração, a partir do qual o administrador pode adicionar e remover os produtos do site.

A ASP.NET Web Forms fornece nativamente capacidades de membership. Ao usar o modelo padrão, temos uma funcionalidade pronta que podemos usar imediatamente quando o aplicativo é executado. Este artigo mostra como usar o recurso membership da ASP.NET para adicionar uma função de administrador e atribuir um usuário para esse papel. Vamos aprender como restringir o acesso na pasta de administração, e vamos adicionar uma página para a pasta de administração, que permite ao administrador adicionar e remover produtos e também visualizar um produto depois que ele foi adicionado.

O que você vai aprender:

  • Como usar o código para adicionar uma função de administrador e um usuário com a aplicação;
  • Como restringir o acesso na pasta e na página de administração;
  • Como fornecer navegação para a função de administrador;
  • Como usar o modelo de ligação (model binding) para preencher um controle DropDownList com categorias de produtos;
  • Como fazer upload de um arquivo para a aplicação web usando o controle FileUpload;
  • Como usar controles de validação para implementar a validação de entrada;
  • Como adicionar e remover produtos a partir da aplicação.

Neste artigo iremos usar os seguintes recursos:

  • Membership ASP.NET
  • Configuração e autorização
  • modelo de ligação (Model Binding)
  • Validation Unobstrusive (validação discreta)

Este artigo foi integralmente baseado no artigo original: http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/membership-and-administration (com algumas alterações e portado para a linguagem VB .NET).

Incluindo um usuário com perfil de administrador

Usando o recurso membership da ASP.NET, é fácil adicionar uma função de administrador e atribuir um usuário para esse papel usando via código.

Vamos começar verificando se os perfis (roles) estão habilitados. Na janela Solution Explorer, localize e abra o arquivo Web.config na raíz da aplicação web.

Vamos localizar o nó roleManager e incluir o atributo enabled= true de forma a ativar este recurso. O código deverá ficar conforme abaixo:

&lt;roleManager enabled="true" defaultProvider="DefaultRoleProvider"&gt;

<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">Agora vamos abrir o arquivo Global.asax.cs e adicionar o código destacado abaixo para evento Application_Start:</span>

aspn_4571

Dessa forma, na próxima vez que a aplicação for executada, será criado um usuário Admin, com senha 123&$#, como administrador da nossa aplicação.

Restringindo o acesso à área e à página de administração

Nossa aplicação permite que usuários anônimos e registrados acessem o site, visualizem os produtos e realizem compras. O usuário administrador que acabamos de definir, quando autenticado, deverá ter acesso a uma área restrita para poder incluir e remover produtos.

Vamos incluir uma nova pasta chamada Admin na aplicação que será a nossa área restrita com acesso exclusivo do administrador e vamos incluir nesta pasta a página AdminPage.aspx.

Clique com o botão direito sobre o nome do projeto e selecione a opção Add -> New Folder e informe o nome Admin. Agora, selecione a pasta Admin e clique com o botão direito do mouse sobre ele e clique em Add -> New Item. Selecione o template Visual Basic->Web e, a seguir, Web Form using Master Page e informe o nome AdminPage.aspx e clique no botão Add.

aspn_4572

Selecione a master page Site.Master e clique em OK. Agora, vamos incluir na pasta Admin um novo arquivo web.config, de forma a restringir o acesso às páginas contidas nesta pasta. Selecione a pasta Admin e no menu Project clique em Add New Item. Depois selecione Visual Basic -> Web e o template Web Configuration File. Aceite o nome Web.Config e clique no botão Add.

aspn_4573

Substitua o código do arquivo Web.Config incluindo o código a seguir neste arquivo:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <authorization>
      <allow roles="Administrator"/>
      <deny users="*"/>
    </authorization>
  </system.web>
</configuration>.

Este código faz com que somente os usuários com perfil de administrador possam acessar a pasta Admin.

Incluindo a navegação do administrador

Para permitir ao administrador navegar para a área de administração da aplicação, vamos adicionar um link na página Site.Master. Apenas os usuários que pertencem à função de administrador serão capaz de ver o link do administrador e acessar a seção de administração.

Para criar um link para administradores, abra o arquivo Site.master e adicione a marcação em destaque ao elemento nav, conforme abaixo:

aspn_4574

Agora, abra o arquivo Site.Master.vb e adicione o código a seguir no evento Load. assim você fará o link Admin visível apenas para administradores:

aspn_4575

Quando a página for carregada, o código verificará se o usuário logado tem o perfil de administrador e, se o usuário for um administrador, o elemento span com o link para a páginaAdminPage.aspx (e consequentemente o link dentro do span) ficará visível.

Habilitando a administração de produtos

Criamos um perfil de administrador e acrescentamos um usuário administrador, uma pasta de administração e uma página de administração. Definimos também os direitos de acesso para a pasta de administração e da página, e adicionamos um link de navegação para o administrador.

Abra a página Adminpage.aspx na pasta Admin e substitua o código pelo código abaixo:

<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="AdminPage.aspx.vb" Inherits="WingTipToys.AdminPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="FeaturedContent" runat="server">
    <h1>Administração</h1>
    <hr />
    <h3>Incluir Produto:</h3>
    <table>
        <tr>
            <td><asp:Label ID="LabelAddCategoria" runat="server">Categoria:</asp:Label></td>
            <td>
                <asp:DropDownList ID="DropDownAddCategory" runat="server" 
                    ItemType="WingtipToys.WingtipToys.Models.Categoria" 
                    SelectMethod="GetCategorias" DataTextField="CategoriaNome" 
                    DataValueField="CategoriaID" >
                </asp:DropDownList>
            </td>
        </tr>
        <tr>
            <td><asp:Label ID="LabelAddNome" runat="server">Nome:</asp:Label></td>
            <td>
                <asp:TextBox ID="AddProdutoNome" runat="server" Width="217px"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" Text="* Nome do Produto é obrigatório." ControlToValidate="AddProdutoNome" 
SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
            </td>
        </tr>
        <tr>
            <td><asp:Label ID="LabelAddDescricao" runat="server">Descrição:</asp:Label></td>
            <td>
                <asp:TextBox ID="AddProdutoDescricao" runat="server" Width="213px"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" Text="* Descrição é obrigatória." ControlToValidate="AddProdutoDescricao" 
SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
            </td>
        </tr>
        <tr>
            <td><asp:Label ID="LabelAddPreco" runat="server">Preço:</asp:Label></td>
            <td>
                <asp:TextBox ID="AddProdutoPreco" runat="server" Width="213px"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" Text="* Preço deve ser informado." ControlToValidate="AddProdutoPreco" 
SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                <asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" Text="* O preço deve ser válido (não use R$)." ControlToValidate="AddProdutoPreco" 
SetFocusOnError="True" Display="Dynamic" ValidationExpression="^[0-9]*(\.)?[0-9]?[0-9]?$"></asp:RegularExpressionValidator>
            </td>
        </tr>
        <tr>
            <td><asp:Label ID="LabelAddImagemArquivo" runat="server">Arq. Imagem:</asp:Label></td>
            <td>
                <asp:FileUpload ID="ProdutoImagem" runat="server" Width="359px" />
                <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" Text="* Imagem deve ser informada." ControlToValidate="ProdutoImagem" 
SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
            </td>
        </tr>
    </table>
    <br />
    <asp:Button ID="AddProdutoButton" runat="server" Text="Adicionar Produto" OnClick="AddProdutoButton_Click"  CausesValidation="true"/>
    <asp:Label ID="LabelAddStatus" runat="server" Text=""></asp:Label>
    <br />
    <h3>Remover Produto:</h3>
    <table>
        <tr>
            <td><asp:Label ID="LabelRemoveProduct" runat="server">Product:</asp:Label></td>
            <td><asp:DropDownList ID="DropDownRemoveProduto" runat="server" ItemType="WingtipToys.WingtipToys.Models.Produto" 
                    SelectMethod="GetProdutos" AppendDataBoundItems="true" 
                    DataTextField="ProdutoNome" DataValueField="ProdutoID" >
                </asp:DropDownList>
            </td>
        </tr>
    </table>
    <asp:Button ID="RemoveProdutoButton" runat="server" Text="Remover Produto" OnClick="RemoveProdutoButton_Click" CausesValidation="false"/>
    <asp:Label ID="LabelRemoveStatus" runat="server" Text=""></asp:Label>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
</asp:Content>

A seguir, abra o arquivo code-behind AdminPage.aspx.vb e adicione o código a seguir neste arquivo:

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports WingTipToys.WingtipToys.Models
Imports WingTipToys.WingtipToys.Logic

Public Class AdminPage
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim produtoAction As String = Request.QueryString("ProdutoAction")
        If produtoAction = "add" Then
            LabelAddStatus.Text = "Produto Incluído!"
        End If

        If produtoAction = "remove" Then
            LabelRemoveStatus.Text = "Produto removido!"
        End If
    End Sub

    Protected Sub AddProductButton_Click(sender As Object, e As EventArgs)
        Dim fileOK As [Boolean] = False
        Dim path As [String] = Server.MapPath("~/Catalog/Images/")
        If ProdutoImagem.HasFile Then
            Dim fileExtension As [String] = System.IO.Path.GetExtension(ProdutoImagem.FileName).ToLower()
            Dim allowedExtensions As [String]() = {".gif", ".png", ".jpeg", ".jpg"}
            For i As Integer = 0 To allowedExtensions.Length - 1
                If fileExtension = allowedExtensions(i) Then
                    fileOK = True
                End If
            Next
        End If

        If fileOK Then
            Try
                ' Salva imagens na pasta.
                ProdutoImagem.PostedFile.SaveAs(path + ProdutoImagem.FileName)
                ' Salva imagens na pasta Images/Thumbs.
                ProdutoImagem.PostedFile.SaveAs(path + "Thumbs/" + ProdutoImagem.FileName)
            Catch ex As Exception
                LabelAddStatus.Text = ex.Message
            End Try

            ' Inclui os produtos no DB.
            Dim produtos As New AddProdutos()
            Dim addSucesso As Boolean = produtos.AddProduto(AddProdutoNome.Text, AddProdutoDescricao.Text, AddProdutoPreco.Text, 
DropDownAddCategory.SelectedValue, ProdutoImagem.FileName)
            If addSucesso Then
                ' recarrega a pagina
                Dim pageUrl As String = Request.Url.AbsoluteUri.Substring(0, Request.Url.AbsoluteUri.Count() - Request.Url.Query.Count())
                Response.Redirect(pageUrl + "?ProdutoAction=add")
            Else
                LabelAddStatus.Text = "Não foi possível incluir um novo produto no banco de dados."
            End If
        Else
            LabelAddStatus.Text = "Não foi possível aceitar o tipo do arquivo."
        End If
    End Sub

    Public Function GetCategorias() As IQueryable
        Dim db = New ProdutoContexto()
        Dim query As IQueryable = db.Categorias
        Return query
    End Function

    Public Function GetProdutos() As IQueryable
        Dim db = New ProdutoContexto()
        Dim query As IQueryable = db.Produtos
        Return query
    End Function

    Protected Sub RemoveProdutoButton_Click(sender As Object, e As EventArgs)
        Dim db = New ProdutoContexto()
        Dim produtoId As Integer = Convert.ToInt16(DropDownRemoveProduto.SelectedValue)
        Dim myItem = (From c In db.Produtos Where c.ProdutoID = produtoId Select c).FirstOrDefault()
        If myItem IsNot Nothing Then
            db.Produtos.Remove(myItem)
            db.SaveChanges()

            ' recarrega a página
            Dim pageUrl As String = Request.Url.AbsoluteUri.Substring(0, Request.Url.AbsoluteUri.Count() - Request.Url.Query.Count())
            Response.Redirect(pageUrl + "?ProdutoAction=remove")
        Else
            LabelRemoveStatus.Text = "Não foi possível localizar o produto."
        End If
    End Sub

End Class

No código que inserimos no arquivo code-behind AdminPage.aspx.vb temos uma classe chamada AddProdutos que faz o trabalho de adicionar produtos à base de dados. Esta classe não existe ainda, então, vamos criá-la agora.

Clique com o botão direito do mouse sobre a pasta Logic e selecione Add -> Class. Selecione o Visual Basic e o template Class, informe o nome AddProduto.vb e clique no botão Add.

aspn_4576

A seguir, inclua o código abaixo no arquivo AddProdutos.vb na classe AddProduto:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports WingTipToys.WingtipToys.Models

Namespace WingtipToys.Logic
    Public Class AddProdutos

        Public Function AddProduto(ProdutoNome As String, ProdutoDesc As String, ProdutoPreco As String, 
ProdutoCategoria As String, ProdutoImagemPath As String) As Boolean
            Dim meuProduto = New Produto()
            meuProduto.ProdutoNome = ProdutoNome
            meuProduto.Descricao = ProdutoDesc
            meuProduto.PrecoUnitario = Convert.ToDouble(ProdutoPreco)
            meuProduto.CaminhoImagem = ProdutoImagemPath
            meuProduto.CategoriaID = Convert.ToInt32(ProdutoCategoria)

            ' pega o contexto
            Dim _db As New ProdutoContexto()

            ' Inclui o produto no DB.
            _db.Produtos.Add(meuProduto)
            _db.SaveChanges()

            ' ok
            Return True
        End Function

    End Class
End Namespace

A página AdminPage.aspx permite ao administrador adicionar e remover produtos. Quando um novo produto é adicionado, os detalhes sobre o produto são validados e, em seguida, gravados no banco de dados. Após isso o novo produto está disponível imediatamente para todos os usuários da aplicação.

Validação discreta (Unobtrusive Validation)

Os detalhes do produto que o usuário fornece na página AdminPage.aspx são validados usando controles de validação (RequiredFieldValidator e RegularExpressionValidator). Esses controles automaticamente usam a validação discreta, que permite que os controles de validação usem JavaScript para a lógica da validação do lado do cliente – o que significa que a página tem que ir até o servidor para ser validada. Por padrão, a validação discreta está incluída no arquivo Web.config, com base na seguinte configuração:

<add key="ValidationSettings:UnobtrusiveValidationMode" value="WebForms" />

Expressões regulares

O preço do produto na página AdminPage.aspx é validado usando um controle RegularExpressionValidator. Este controle valida se o valor do controle de entrada associado (o TextBox “AddProdutPreco”) corresponde ao padrão especificado pela expressão regular. Uma expressão regular é uma notação de correspondência de padrão que permite encontrar rapidamente e combinar padrões de caracteres específicos. O controle RegularExpressionValidator inclui uma propriedade chamada ValidationExpression, que contém a expressão regular usada para validar a entrada de preços, como mostrado abaixo:

<asp:RegularExpressionValidator 
   ID="RegularExpressionValidator1" runat="server" 
   Text="* O preço deve ser válido (não use R$)." ControlToValidate="AddProdutoPreco" 
   SetFocusOnError="True" Display="Dynamic" 
   ValidationExpression="^[0-9]*(\.)?[0-9]?[0-9]?$">
</asp:RegularExpressionValidator>

Controle FileUpload

Além da entrada e controles de validação, usamos o controle FileUpload na página AdminPage.aspx. Esse controle fornece a capacidade de fazer upload de arquivos. Neste caso, você está apenas permitindo que os arquivos de imagem possam ser carregados. No arquivo code-behind (AdminPage.aspx.vb), quando o botão AddProdutoButton for clicado, o código verificará a propriedade HasFile do controle FileUpload. Se o controle tem um arquivo e se o tipo de arquivo (com base na extensão do arquivo) for permitido, a imagem é guardada na pasta Images na pasta Images/Thumbs da aplicação.

Usando o Model Binding

Nós já usamos o Model Binding para preencher os controles ListView, FormsView, GridView e o controle DetailView. Neste artigo, vamos usar o modelo de ligação para preencher um controle DropDownList com uma lista de categorias de produtos.

A marcação que você adicionou no arquivo AdminPage.aspx contém um controle DropDownList chamado DropDownAddCategoria:

<asp:DropDownList ID="DropDownAddCategoria" runat="server" 
ItemType="WingtipToys.WingtipToys.Models.Categoria" 
SelectMethod="GetCategorias" DataTextField="CategoriaNome" 
DataValueField="CategoriaID" >
</asp:DropDownList>

Usamos o modelo de ligação para preencher o DropDownList definindo o atributo ItemType e o atributo SelectMethod. O atributo ItemType especifica que vamos usar o tipo Categoria ao preencher o controle. Definimos este tipo no início desta série, criando a classe Categoria, que está na pasta Models dentro do arquivo Categoria.vb.

O atributo SelectMethod do controle DropDownList especifica que usamos o método GetCategorias(abaixo), que está incluído no arquivo code-behind (AdminPage.aspx.vb):

Public Function GetCategorias() As IQueryable
    Dim db = New ProdutoContexto()
    Dim query As IQueryable = db.Categorias
    Return query
End Function

Este método especifica que uma interface IQueryable é usada para avaliar uma consulta contra um tipo de categoria. O valor retornado é utilizado para preencher o DropDownList na marcação da página (AdminPage.aspx).

O texto apresentado para cada item da lista é especificado, definindo o atributo DataTextField. O atributo DataTextField usa a propriedade CategoriaNome da classe Categoria para mostrar cada categoria no controle DropDownList. O valor real que é passado quando um item é selecionado no controle DropDownList é baseado no atributo DataValueField. O atributo DataValueField está definido para a propriedade CategoriaID como definido na classe categoria.

Como a aplicação vai funcionar

Quando o administrador navegar para a página, pela primeira vez, o controle DropDownList DropDownAddCategoria é preenchido, tal como descrito acima.

O controle DropDownList DropDownRemoveProduto também é preenchido com os produtos usando a mesma abordagem. O administrador seleciona o tipo de categoria e acrescenta detalhes do produto (nome, descrição, preço e Arquivo de Imagem).

Quando o administrador clicar no botão Adicionar Produto, o manipulador de eventos AddProdutoButton_Click é acionado; esse manipulador, localizado no arquivo code-behindAdminPage.aspx.vb, verifica o arquivo de imagem para se certificar de que ele coincide com os tipos de arquivos permitidos (.gif,. jpg, .png, ou .jpeg).

Em seguida, o arquivo de imagem é salvo em uma pasta da aplicação. A seguir, o novo produto é adicionado à base de dados. Para realizar a adição de um novo produto, uma nova instância da classe AddProdutos é criada e nomeada produtos. A classe AddProdutos tem um método chamado AddProduto, e os objetos produtos chama este método para adicionar o produto à base de dados.

Se um novo produto foi adicionado com sucesso ao banco de dados, a página é recarregada usando o valor da query string: ProdutoAction=add.

Quando a página é recarregada, a sequência de consulta está incluída na URL. Ao recarregar a página, o administrador pode ver imediatamente as atualizações nos controlesDropDownList na página AdminPage.aspx. Além disso, ao incluir a sequência de consulta com a URL, a página pode exibir uma mensagem de sucesso para o administrador.

Quando a página AdminPage.aspx for recarregada o evento Load é chamado e então verifica a query string informando se o produto foi incluído com sucesso.

Testando a aplicação

Vamos executar o aplicativo agora para ver como podemos adicionar, excluir e atualizar os itens no carrinho de compras. O total do carrinho de compras irá refletir o custo total de todos os itens no carrinho de compras.

Pressione CTRL+F5 para rodar a aplicação e será apresentada a página Default.aspx. Clique no link para realizar o Log in.

aspn_4577

A página Login.aspx será apresentada. Vamos fazer o login com o perfil de administrador, informando o nome de usuário e a senha que criamos para o perfil de administrador.

aspn_4578

Ao verificar que o usuário tem o perfil de administrador, a página Default.aspx é recarregada exibindo agora o link Admin e o nome do usuário logado:

aspn_4579

Clique no link Admin para acessar a área do administrador. A página AdminPage.aspx será apresentada com as opções para adicionar e remover um produto:

aspn_457a

Vamos incluir um produto escolhendo uma categoria e fornecendo alguns dados e o arquivo de imagem.

Eu escolhi a categoria Carros e informei os dados para Ford Fiesta e a imagem de um veículo desta marca:

aspn_457b

Após isso, podemos verificar e constatar que o produto foi realmente incluindo, verificando a relação dos produtos para a categoria Carros:

aspn_457c (1)

Clique sobre a imagem para exibir os seus detalhes e assim verificarmos se os dados informados foram gravados:

aspn_457d

Tudo ok! Assim confirmamos que nossa implementação está funcionando corretamente.

O post ASP .NET 4.5 Web Forms – Criando a área de Administração – Parte 07 apareceu primeiro em .

ASP .NET 4.5 Web Forms – Implementando o suporte ao roteamento via URL – Parte 08

$
0
0

Neste artigo, vamos modificar a nossa aplicação para suportar o roteamento de URL. O roteamento permite que sua aplicação web utilize URLs que são mais amigáveis, mais fáceis de lembrar e melhor suportadas pelos motores de busca.

O roteamento de URL foi introduzida com o objetivo de expor URLs limpas e mais amigáveis para os motores de busca no padrão “web 2.0″. O roteamento de URL permite que você configure uma aplicação para aceitar URLs de requisição que não mapeiam para arquivos físicos. Em vez disso, você pode usar o roteamento para definir URLs que são semanticamente significativas para os usuários e que podem ajudar no que diz respeito à otimização de motores de busca (SEO).

O que você vai aprender:

  • Como registrar rotas para uma aplicação ASP.NET Web Forms;
  • Como adicionar rotas estáticas para uma página web;
  • Como adicionar rotas dinâmicas para uma página web;
  • Como selecionar dados de um banco de dados para apoiar rotas dinâmicas.

Este artigo foi integralmente baseado no artigo original: http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/url-routing (com algumas alterações e portado para a linguagem VB .NET).

Conceitos sobre o roteamento ASP .NET

O roteamento de URL permite que você configure um aplicativo para aceitar URLs que não mapeiam para arquivos físicos. Um pedido de URL é simplesmente uma URL que um usuário digita em seu navegador para encontrar uma página em seu site. Você usa o roteamento para definir URLs que são semanticamente significativas para os usuários e que pode ajudar nos mecanismos de buscas como Search Engine Optimization (SEO).

Obs: O termo SEO ou otimização para mecanismos de pesquisa (português brasileiro) é o conjunto de estratégias com o objetivo de potencializar e melhorar o posicionamento de um site nas páginas de resultados naturais (orgânicos) nos sites de busca.

Em nossa aplicação atual, a URL usada para acessar um produto é escrita da seguinte forma: http://localhost:1234/ProdutoDetalhes.aspx?produtoID=2

Com a implementação do roteamento de URL, a nossa aplicação poderá ser acessada usando a seguinte URL: http://localhost:1234/Produto/Convertible%20Car

A seguir, veremos alguns conceitos importantes que usaremos em nossa implementação.

Rotas

Uma rota é um padrão de URL que está mapeada para um manipulador. O manipulador pode ser um arquivo físico, como um arquivo .aspx em um aplicativo Web Forms. O manipulador pode ser também uma classe que processa o pedido. Para definir uma rota, você cria uma instância da classe Route, especificando o padrão de URL, o manipulador e, opcionalmente, um nome para a rota.

Você pode adicionar a rota para a aplicação, adicionando o objeto Route para a propriedade estática Routes da classe RouteTable. A propriedade Routes é um objetoRouteCollection que armazena todas as rotas para a aplicação.

Padrões de URL

Um padrão de URL pode conter valores literais e marcadores de posição variáveis (conhecido como parâmetros de URL). Os literais e os marcadores estão localizados em segmentos da URL que são delimitados pelo caractere barra (/).

Quando uma solicitação para sua aplicação web é feita, a URL é analisada em segmentos e espaços reservados, e os valores das variáveis são fornecidos para o manipulador de solicitação. Este processo é semelhante à maneira como os dados em uma sequência de consulta é analisado e passado para o manipulador de solicitação. Em ambos os casos, a informação variável é incluída na URL e passada para o manipulador, sob a forma de um par chave-valor.

Para sequências de consulta, tanto as chaves como os valores estão na URL. Para as rotas, as chaves são os nomes de espaços reservados definidos no padrão da URL, e apenas os valores estão na URL.

Em um padrão de URL, você define espaços reservados, colocando-os entre chaves ({e}). Você pode definir mais do que um espaço reservado em um segmento, mas os espaços reservados devem ser separados por um valor literal. Por exemplo, {idioma}-{país}/{ação} é um padrão de rota válida. No entanto {idioma}{país}/{ação} não é um padrão válido, porque não existe um valor literal ou delimitador entre os espaços reservados.

Mapeando e registrando rotas

Antes de podermos incluir rotas para páginas da nossa aplicação web temos que registrar as rotas quando a aplicação web for iniciada.

Para registrar as rotas, vamos modificar o manipulador de eventos Application_Start do arquivo Global.asax.

Abra o arquivo Global.asax.vb e altere o seu código conforme o baixo. Onde você deve incluir o código destacado em negrito:

Imports System.Web.Optimization
Imports System.Data.Entity
Imports WingTipToys.WingtipToys.Models
<strong>Imports System.Web.Routing</strong>

Public Class Global_asax
    Inherits HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the application is started
        BundleConfig.RegisterBundles(BundleTable.Bundles)
        AuthConfig.RegisterOpenAuth()
        Database.SetInitializer(New ProdutoDatabaseInitializer())

        ' Inclui um Administrator.
        If Not Roles.RoleExists("Administrator") Then
            Roles.CreateRole("Administrator")
        End If

        If Membership.GetUser("Admin") Is Nothing Then
            Membership.CreateUser("Admin", "123456", "macoratti@yahoo.com")
            Roles.AddUserToRole("Admin", "Administrator")
        End If

        <strong>'adicionando rotas</strong>
<strong>        RegisterRoutes(RouteTable.Routes)</strong>

    End Sub

    <strong>Private Sub RegisterRoutes(ByVal routes As RouteCollection)</strong>
<strong>        routes.MapPageRoute("HomeRoute", "Home", "~/Default.aspx")</strong>
<strong>        routes.MapPageRoute("AboutRoute", "About", "~/About.aspx")</strong>
<strong>        routes.MapPageRoute("ContatoRoute", "Contato", "~/Contact.aspx")</strong>
<strong>        routes.MapPageRoute("ProdutoListaRoute", "ProdutoLista", "~/ProdutoLista.aspx")</strong>

<strong>        routes.MapPageRoute("ProdutosPorCategoriaRoute", "ProdutoLista/{categoriaNome}", "~/ProdutoLista.aspx")</strong>
<strong>        routes.MapPageRoute("ProdutoPorNomeRoute", "Produto/{produtoNome}", "~/ProdutoDetalhes.aspx")</strong>
<strong>    End Sub</strong>

    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires at the beginning of each request
    End Sub

    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires upon attempting to authenticate the use
    End Sub

    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when an error occurs
    End Sub

    Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the application ends
    End Sub
End Class

Quando nossa aplicação for iniciada, ela chama o manipulador de eventos Application_Start. No final deste processador de eventos, o método RegisterRoutes é chamado. O métodoRegisterRoutes adiciona cada rota, chamando o método MapPageRoute do objeto RouteCollection.

O método MapPageRoute é usado para registrar tanto as rotas estáticas como as rotas dinâmicas. As rotas estáticas e dinâmicas são definidas usando um nome de rota, uma URL de rota e uma URL física. A seguir, temos um exemplo de rota estática: routes.MapPageRoute(“HomeRoute”, “Home”, “~/Default.aspx”)

Em uma rota estática temos a seguinte composição:

  • O primeiro parâmetro (“HomeRoute”) é o nome da rota. Ele é usado para chamar a rota quando for necessário;
  • O segundo parâmetro (“Home”) é a URL da rota. É a URL de substituição amigável que é mostrada como parte da URL;
  • O terceiro parâmetro (“~/Default.aspx”) é o caminho real para o conteúdo que será exibido.

Usando o HomeRoute, o link Home irá navegar para a seguinte URL: http://localhost:1234/Home

Uma rota dinâmica é semelhante a uma rota estática. No entanto, o segundo parâmetro que define a URL de substituição amigável pode ser dinâmico e baseado em código. Você usa rotas dinâmicas quando você está preenchendo um controle de dados com links que são gerados com base nos seus dados.

Um exemplo de rota dinâmica é mostrado a seguir: routes.MapPageRoute(“ProdutosPorCategoriaRoute”, “ProdutoLista/{categoriaNome}”, “~/ProdutoLista.aspx”).

O segundo parâmetro da rota dinâmica inclui um valor dinâmico especificado por chaves ({}). Neste caso, categoriaNome é uma variável que será utilizada para determinar o percurso de encaminhamento adequado.

Recuperando e usando dados de rota

Como mencionado acima, tanto as rotas estáticas como as dinâmicas podem ser definidas. O código que incluímos no manipulador de eventos Application_Start no arquivoGobal.asax.vb carrega as rotas estáticas e as dinâmicas.

Configurando rotas estáticas

Podemos usar o método GetRouteUrl para renderizar uma rota. Para rotas estáticas, passamos para o método GetRouteUrl o nome da rota como o primeiro parâmetro e um espaço reservado nulo (nothing) como o segundo parâmetro. Não há valores dinâmicos passados.

Uma rota estática não implementa todos os dados gerados dinamicamente. Não usamos as rotas estáticas com um controle de dados. Em vez disso, usamos o método GetRouteUrl para recuperar a rota estática que foi registrada no arquivo Global.asax.cs.

Na janela Solution Explorer abra o arquivo Site.master e atualize o elemento nav da página conforme mostrado a seguir:

aspn_4581

As linhas de código incluídas para definir o roteamento substituíram as seguintes linhas de código:

<li><a href="/">Home</a></li>
<li><a href="/About.aspx">Sobre</a></li>
<li><a href="/Contato.aspx">Contato</a></li>
<li><a href="/ProdutoLista.aspx">Produtos</a></li>

No código passamos o nome da rota quando chamamos o método GetRouteUrl – este método usa o nome da rota para procurar os detalhes da rota que foram registradas no arquivo Global.asax.vb. Os valores retornados são então adicionados a cada link.

Definindo rotas dinâmicas

As rotas dinâmicas exigem que seja definido um código adicional. Usaremos o modelo de ligação (Model Binding) para recuperar um objeto RouteValueDictionary que é usado ao gerar as rotas, utilizando dados de um controle de dados. Esse objeto conterá uma lista de nomes de produtos que pertencem a uma categoria específica de produtos. A ligação é criada para cada produto, com base nos dados e na rota.

Ativando as rotas para Categorias e Produtos

A seguir, vamos atualizar nossa aplicação para usar a rota ProdutosPorCategoriaRoute para determinar a rota correta para incluir para cada link a categoria do produto.

Vamos atualizar também a página ProdutoLista.aspx para incluir um link direcionado para cada produto. As ligações serão exibidas como eram antes da mudança, no entanto os links agora vão usar o roteamento da URL.

Abra a página Site.master e atualize o controle ListView com id igual a categoriaLista, incluindo o código destacado mostrado a seguir:

aspn_4582

Agora abra o arquivo ProdutoLista.aspx e atualize o elemento ItemTemplate com o código destacado exibido a seguir:

aspn_4583

Substitua o método GetProdutos do arquivo code-behind ProdutoLista.aspx.vb com o seguinte código:

Public Function GetProdutos(<QueryString("id")> categoriaId As System.Nullable(Of Integer), <RouteData> categoriaNome As String) As IQueryable(Of Produto)

        Dim _db = New WingtipToys.Models.ProdutoContexto()
        Dim query As IQueryable(Of Produto) = _db.Produtos

        If categoriaId.HasValue AndAlso categoriaId > 0 Then
            query = query.Where(Function(p) p.CategoriaID = categoriaId)
        End If

        If Not [String].IsNullOrEmpty(categoriaNome) Then
            query = query.Where(Function(p) [String].Compare(p.Categoria.CategoriaNome, categoriaNome) = 0)
        End If

        Return query

    End Function

Adicionando código para os detalhes do produto

Agora, atualize o arquivo code-behind ProdutoDetalhes.aspx.vb para que a página ProdutoDetalhes.aspx use os dados de rota.

Observe que o novo método getProduto também aceita um valor de sequência de consulta para o caso em que o usuário tenha um link marcado que usa as antigas URLs não amigáveis.

Substitua o método getProduto com o seguinte código:

 Public Function GetProduto(<QueryString("ProdutoID")> produtoId As System.Nullable(Of Integer), <RouteData> produtoNome As String) As IQueryable(Of Produto)
        Dim _db = New WingtipToys.Models.ProdutoContexto()
        Dim query As IQueryable(Of Produto) = _db.Produtos
        If produtoId.HasValue AndAlso produtoId > 0 Then
            query = query.Where(Function(p) p.ProdutoID = produtoId)
        ElseIf Not [String].IsNullOrEmpty(produtoNome) Then
            query = query.Where(Function(p) [String].Compare(p.ProdutoNome, produtoNome) = 0)
        Else
            query = Nothing
        End If
        Return query
    End Function

Rodando 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

Clique no link Produtos no topo da página. Todos os produtos são apresentados na página ProdutoLista.aspx. A seguinte URL (usando o seu número de porta) é exibida para o navegador: http://localhost:64786/ProdutoLista

aspn_4585

A seguir, clique no link de categoria Carros perto do topo da página.

Apenas carros são exibidos na página ProdutoLista.aspx. A seguinte URL é exibida para o navegador: http://localhost:1234/ProdutoLista/Carros

aspn_4586

Clique no link com o nome do primeiro carro listado na página - Convertible Car (“carro conversível”) para exibir os detalhes do produto.

Os detalhes do carro será exibido e a seguinte URL (usando o seu número de porta) é exibida para o navegador: http://localhost:1234/Produto/Convertible 20Car%

aspn_4587

Agora vamos fazer um teste: digite a seguinte URL não roteada (usando o seu número de porta) no seu navegador: http://localhost:xxxx/ProdutoDetalhes.aspx?produtoID=2

O código ainda reconhece uma URL que inclui uma sequência de consulta, para o caso em que um usuário tenha um link marcado em seu bookmark.

aspn_4588

Neste artigo, adicionamos rotas estáticas e rotas dinâmicas para categorias e produtos.

Você aprendeu como definir rotas estáticas e dinâmicas e também como as rotas dinâmicas podem ser integradas com controles de dados que usam o modelo de ligação. No próximo artigo, vamos implementar o tratamento de erro global em nossa aplicação.

 

O post ASP .NET 4.5 Web Forms – Implementando o suporte ao roteamento via URL – Parte 08 apareceu primeiro em .

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

$
0
0

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:

<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:

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.

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:

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:

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:

<%@ 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:

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:

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:

 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:

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.

***

Colaboração de Vitor Hugo.

Neste artigo aprendemos como manipular erros ao nível da aplicação, a nível da página, e do nível de código. Aprendemos também a registrar erros tratados e sem tratamento para posterior análise. Além disso, aprendemos  sobre a importância das mensagens de erro seguras.

E com isso encerramos essa série de tutoriais onde acompanhamos a criação de uma aplicação ASP .NET Web Forms usando o Visual Studio Express For Web 2012.

Embora a aplicação possa ser melhorada em muitos aspectos, o objetivo foi mostrar como podemos criar aplicações dinâmicas com acesso a dados usando recursos integrados do Visual Studio.

Espero que esses artigos tenham contribuído para aguçar o seu desejo em aprender mais sobre a tecnologia ASP .NET, pois o que vimos foi só a ponta do Iceberg.

Bom estudo!!!

O post ASP .NET 4.5 Web Forms – Tratamento de erros e registro de log – Parte 09 apareceu primeiro em .


.NET – Novos recursos do Visual Studio 2012

$
0
0

Neste artigo eu mostro alguns recursos presentes no Visual Studio 2012 (e versões Express) que embora simples, podem ajudar muito o desenvolvedor no seu dia a dia.

Vamos a eles…

1. Manipulador de eventos

Quando você incluir um controle ASP .NET via código, terá acesso ao IntelliSense do Visual Studio que exibirá a opção <Create New Event> a qual cria um manipulador de eventos no código com a assinatura correta.

net_nrvs1

Clicando na sugestão do IntelliSense teremos o nome do evento criado, e no code-behind, o respectivo código com a assinatura definida:

net_nrvs2

net_nrvs3

2. Exibindo imagens no Solution Explorer

Quando você utilizar imagens em seus projetos referenciados na janela Solution Explorer, você pode visualizar a imagem posicionando o mouse sobre seu nome:

net_nrvs4

3. Exibindo as cores em arquivos de estilo (CSS)

Quando você estiver criando ou editando arquivos de estilos (CSS) tem agora a seu dispor uma ajuda para definir o nome da cor. Quando estiver definindo uma cor ao digitar o símbolo # será aberto uma pequena janela exibindo as cores disponíveis. Basta selecionar a cor desejada que o respectivo código da cor será inserido no código:

net_nrvs5

4. Suporte a diversos navegadores web

Agora todos os navegadores instalados em seu sistema podem ser usados para testar a sua aplicação Web. Basta selecionar o navegador no menu de opções:

net_nrvs6

5. Biblioteca AntiXSS disponível nativamente

A Biblioteca AntiXSS que fazia parte da plataforma Net 4.0, mas era externa, agora está incluída na versão 4.5 da plataforma .NET. Assim, se a sua página precisa usar formatação HTML, a página se torna insegura e para proteger sua página de ataques de cross-site scripting você precisa incluir biblioteca AntiXSS.

A biblioteca AntiXSS na versão 4.5 inclui as seguintes características externas:

  • HtmlEncode, HtmlFormUrlEncode e HtmlAttributeEncode;
  • XmlAttributeEncode e xmlEncode;
  • UrlEncode e UrlPathEncode (novo);
  • CssEncode.

Assim a ASP.NET 4.5 agora incorpora rotinas de codificação de núcleo a partir da versão 4.0 da biblioteca.

As rotinas de codificação são implementadas pelo tipo AntiXssEncoder no novo namespace System.Web.Security.AntiXss. Você pode usar o tipo AntiXssEncoder diretamente chamando qualquer um dos métodos de codificação estáticas que são implementadas no tipo.

Você pode também configurar um aplicativo ASP.NET para usar a classe AntiXssEncoder por padrão. Para isso, inclua o seguinte atributo no arquivo Web.config:

encoderType="System.Web.Security.AntiXss.AntiXssEncoder,System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

Quando o atributo encoderType for configurado para usar o tipo AntiXssEncoder toda a codificação de saída em ASP.NET usará as novas rotinas de codificação de forma automática.

6. Extraindo controles para criar um User Control

Quando você tiver uma quantidade de controles e uma página web, pode ser interessante extrair esses controles da página e gerar um User Control com os mesmos. Isso facilita a manutenção.

Basta selecionar os controles e clicar com o botão direito do mouse e selecionar a opção => Extract to User Control

net_nrvs7

A seguir salve o controle atribuindo um nome:

net_nrvs8

A referência será automaticamente incluída na página:

net_nrvs9

E estamos conversados!

O post .NET – Novos recursos do Visual Studio 2012 apareceu primeiro em .

.NET – O padrão de projeto Observer

$
0
0

O padrão de projeto (Design Pattern) Observer é um padrão comportamental que representa uma relação de 1-N (de um para muitos) entre objetos. Assim, quando um objeto altera o seu estado, os objetos dependentes serão notificados/informados/avisados e atualizados de forma automática. O padrão possibilita que objetos sejam avisados da mudança de estado de outros eventos ocorrendo em outro objeto.

Podemos dizer de forma simples que o objetivo principal do padrão Observer é manter a consistência entre objetos relacionados sem criar um código fortemente acoplado.

As expressões “acoplamento fraco” ou “acoplamento forte” são comuns em quase todas as discussões sobre projeto de software. O acoplamento entre classes ou subsistemas é uma medida da interconexão entre essas classes ou subsistemas. O acoplamento forte significa que as classes relacionadas precisam conhecer detalhes internos umas das outras, as alterações se propagam pelo sistema, e o sistema é potencialmente mais difícil de entender e manter.

  • Acoplamento é o nível de dependência/conhecimento que pode existir entre as classes;
  • Uma classe com acoplamento fraco não é dependente de muitas classes para fazer o que ele tem que fazer;
  • Uma classe com acoplamento forte depende de muitas outras classes para fazer o seu serviço;
  • Uma classe com acoplamento forte é mais difícil de manter, de entender e de ser reusada;

Em uma definição mais formal, um padrão Observer permite:

“Definir uma dependência um-para-muitos entre objetos para que quando um objeto mudar de estado, todos os seus dependentes sejam notificados e atualizados automaticamente.” [GoF]

Nota: Padrões de projeto comportamentais são padrões que tratam das interações e divisões de responsabilidades entre as classes ou objetos.

O diagrama de classes para o padrão Observer é mostrado na figura abaixo:

01

 

Classes/Objetos participantes do padrão:

  1. Sujeito (subject): Conhece os seus observadores. Um número qualquer de observadores é permitido. Provê uma interface para adicionar/remover observadores;
  2. Observador (observer): Define uma interface de atualização para os objetos que devem ser notificados sobre as mudanças no sujeito;
  3. Sujeito concreto (ConcreteSubject): Armazena o estado de interesse para os objetos de ConcreteObserver; envia uma notificação aos observadores concretos quando seu estado mudar;
  4. Observador concreto (ConcreteObserver): Implementa a interface definida pelo Observador para tratar as notificações.

O mesmo diagrama de classes traduzido:

02

 

Vantagens em usar o padrão Observer

  • Permite um acoplamento mínimo entre o Sujeito(Subjet) e o Observador(Observer)
  • Pode reutilizar Sujeitos sem reutilizar os seus observadores e vice-versa;
  • Os Observadores podem ser adicionados sem modificar o Sujeito;
  • Todo sujeito conhece a sua lista de Observadores;
  • O Sujeito não precisa conhecer a classe concreta de um observador, apenas que cada observador implementa a interface update();
  • O Sujeito e o Observador podem pertencer a diferentes camadas de abstração.

Quando usar o padrão Observer?

  • Quando uma modificação do estado de um objeto implica modificações em outros objetos;
  • Quando um objeto deve ser capaz de notificar outros objetos, mas sem pressupostos sobre os objetos a serem notificados;
  • Quando uma abstração possuir dois aspectos e um depende do outro;
  • Quando não desejamos um forte acoplamento com os objetos que necessitam conhecer estas modificações;

Questões de Implementação do padrão Observer

Como é que o sujeito vai manter o controle de seus observadores?

R: Usando um Array, uma lista ligada etc.

E o que ocorre se um observador quer observar mais de um sujeito?

R: Ele têm que dizer ao observador quem ele é através da interface de atualização.

E quem aciona a atualização?

R: O sujeito, sempre que ele muda de estado; os observadores depois de causar uma ou mais mudanças de estado; algum outro objeto.

Obs: Verifique se o Sujeito atualiza o seu estado antes de enviar notificações.

Quanta informação sobre a mudança deve o Sujeito enviar para os observadores?

R: No modelo Push – bastante, ou no modelo Pull – muito pouca.

Um observador pode ser um Sujeito?

R: Sim, pode.

Implementando o padrão de projeto Observer

Este exemplo foi baseado no livro “Use a Cabeça (head first) Padrões de Projeto”, FREEMAN (2007:81-96).

Para implementar o padrão Observer eu vou usar o seguinte cenário:

  • Imagine que temos Assinantes de um serviço de uma Editora;
  • A editora deverá notificar todos os seus assinantes quando houver uma nova edição;
  • A nova edição será representada por variável booleana _novaEdicao que inicialmente terá o seu valor definido como false;
  • O método alterarEdicao() promove a alteração do estado indicando true quando houver uma nova edição;
  • O método getEdicao() obtém o valor atual de _novaEdicao indicando se há uma nova edição ou não.

Nesse cenário, teremos as seguintes classes:

  1. Sujeito – Interface que define os métodos para Registrar/Remover/Notificar um observador;
  2. Observador – Interface que define o método Atualizar() que informa o observador quando houver uma alteração do estado do Sujeito;
  3. EditoraConcreta – Classe concreta que implementa interface Sujeito;
  4. AssinanteConcreto – Classe concreta que implementa a interface Observador.

O Diagrama de classes gerado é visto abaixo:

03

 

Implementando o padrão Observer

Vamos agora criar as classes e verificar o seu funcionamento.

Eu vou usar o Visual Studio 2012 Express Edition e criar uma aplicação Console chamada PadraoProjetoObserver.

Obs: Estou disponibilizando também o código para VB .NET criado no Visual Studio 2012 Express Edition 

No menu Project, clique em Add Class e informe o nome Sujeito.cs/Sujeito.vb e a seguir digite o código da classe Sujeito conforme abaixo:

C#

namespace PadraoProjetoObserver
{
    interface Sujeito
    {
        void RegistrarObservador(Observador o);
        void RemoverObservador(Observador o);
        void NotificarObservadores();
    }
}

VB.NET

Public Interface Sujeito
	Sub RegistrarObservador(o As Observador)
	Sub RemoverObservador(o As Observador)
	Sub NotificarObservadores()
End Interface

A interface Sujeito de declara os métodos para registrar/remover e notificar um observador

C#

namespace PadraoProjetoObserver
{
    interface Observador
    {
        void Atualizar(Sujeito sujeito);
    }
}

VB.NET

Public Interface Observador
	Sub Atualizar(sujeito As Sujeito)
End Interface

A interface Observador declara o método Atualizar

C#

using System;
using System.Collections.Generic;

namespace PadraoProjetoObserver
{
    class EditoraConcreta : Sujeito
    {
        private List<Observador> observadores = new List<Observador>();
        private bool _novaEdicao = false;

        public void RegistrarObservador(Observador o)
        {
            observadores.Add(o);
        }

        public void RemoverObservador(Observador o)
        {
            observadores.Remove(o);
        }

        public void NotificarObservadores()
        {
            foreach (Observador o in observadores)
            {
                o.Atualizar(this);
            }
        }

        public void alterarEdicao()
        {
            if (_novaEdicao)
                _novaEdicao = false;
            else
                _novaEdicao = true;
            NotificarObservadores();
        }

        public Boolean getEdicao()
        {
            return _novaEdicao;
        }
    }
}

VB.NET

IImports System.Collections.Generic

Public Class EditoraConcreta
    Implements Sujeito

    Private observadores As New List(Of Observador)()
    Private _novaEdicao As Boolean = False

    Public Sub NotificarObservadores() Implements Sujeito.NotificarObservadores
        For Each o As Observador In observadores
            o.Atualizar(Me)
        Next
    End Sub

    Public Sub RegistrarObservador(o As Observador) Implements Sujeito.RegistrarObservador
        observadores.Add(o)
    End Sub

    Public Sub RemoverObservador(o As Observador) Implements Sujeito.RemoverObservador
        observadores.Remove(o)
    End Sub

    Public Sub alterarEdicao()
        If _novaEdicao Then
            _novaEdicao = False
        Else
            _novaEdicao = True
        End If
        NotificarObservadores()
    End Sub

    Public Function getEdicao() As [Boolean]
        Return _novaEdicao
    End Function
End Class

Note que na classe concreta EditoraConcreta estamos chamando o método NotificarObservadores() após ocorrer uma mudança de estado no objeto EditoraConcreta;

Na implementação do método notificarObservadores, percorremos os observadores informando cada instância dele mesmo e informando que o objeto que estava sendo observado mudou seu estado.

C#

using System;

namespace PadraoProjetoObserver
{
    class AssinanteConcreto : Observador
    {
        private EditoraConcreta objetoObservado;

        public AssinanteConcreto(EditoraConcreta o)
        {
            objetoObservado = o;
            objetoObservado.RegistrarObservador(this);
        }

        public void Atualizar(Sujeito sujeito)
        {
            if (sujeito == objetoObservado)
            {
                Console.WriteLine("[Aviso] - A editora alterou o seu estado para : "  + objetoObservado.getEdicao());
            }
        }
    }
}

VB.NET

Public Class AssinanteConcreto
    Implements Observador

    Private objetoObservado As EditoraConcreta

    Public Sub New(o As EditoraConcreta)
        objetoObservado = o
        objetoObservado.RegistrarObservador(Me)
    End Sub

    Public Sub Atualizar(sujeito As Sujeito) Implements Observador.Atualizar
        If sujeito.Equals(objetoObservado) Then
            Console.WriteLine("[Aviso] - A editora alterou o seu estado para : " & objetoObservado.getEdicao())
        End If
    End Sub
End Class

No construtor da classe AssinanteConcreto, criamos o observador e já definimos como parâmetro o objeto observado, logo a seguir podemos chamar o método adicionarObservador que passa por referência a sua própria instância.

Quando o método atualizar é chamado, nós precisamos verificar se a editora que alterou o estado é a mesma que estamos observando.

C#

using System;

namespace PadraoProjetoObserver
{
    class Program
    {
        static void Main(string[] args)
        {
            EditoraConcreta editora = new EditoraConcreta();
            // Editora ja inicia com valor padrão igual a false
            AssinanteConcreto assinante1 = new AssinanteConcreto(editora);
            AssinanteConcreto assinante2 = new AssinanteConcreto(editora);
            AssinanteConcreto assinante3 = new AssinanteConcreto(editora);
            AssinanteConcreto assinante4 = new AssinanteConcreto(editora);
            AssinanteConcreto assinante5 = new AssinanteConcreto(editora);

            // Já passando a editora como parametro
            editora.alterarEdicao();
            // Nesse momento é chamado o método atualizar
            // das instâncias assinante1 e assinante2, resultadao:
            // [Aviso] A Editora mudou seu estado para: true (5 x) 
            editora.alterarEdicao();
            //[Aviso] A Editora mudou seu estado para: false (5 x)
            // Obs: temos 5 saídas porque temos 5 assinantes
            Console.ReadKey();
        }
    }
}

VB.NET

Module Module1

 Sub Main()
 Dim editora As New EditoraConcreta()
' Editora ja inicia com valor padrão igual a false

 Dim assinante1 As New AssinanteConcreto(editora)
 Dim assinante2 As New AssinanteConcreto(editora)
 Dim assinante3 As New AssinanteConcreto(editora)
 Dim assinante4 As New AssinanteConcreto(editora)
 Dim assinante5 As New AssinanteConcreto(editora)
 ' Já passando a editora como parametro

 editora.alterarEdicao()
 ' Nesse momento é chamado o método atualizar
 ' das instâncias assinante1 e assinante2, resultadao:
 ' [Aviso] A Editora mudou seu estado para: true (5 x) 
 editora.alterarEdicao()
 '[Aviso] A Editora mudou seu estado para: false (5 x)
 ' Obs: temos 5 saídas porque temos 5 assinantes
 Console.ReadKey()  
End Sub
End Module

Poderíamos dar nomes aos assinantes usando uma string em AssinanteConcreto e passar seu nome para o construtor como segundo parâmetro.

04

O resultado acima mostra a notificação emitida a todos os assinantes registrados após a mudança de estado.

Pegue a solução completa aqui:

PadraoProjetoObserver.zip 

PadraoProjetoObserverVB.zip

O post .NET – O padrão de projeto Observer apareceu primeiro em .

ADO .NET – Executando múltiplos comandos em uma única conexão

$
0
0

Se você usa o objeto DataReader em seus aplicativos e tentou utilizar mais de um comando em uma conexão aberta, já deve ter visto a mensagem de erro : “There is already an open DataReader associated with this Command which must be closed first.” (“Já existe um DataReader aberto associado a esta conexão que deve ser fechado….” )

Então não posso executar mais de um comando em uma mesma conexão???

Pode, mas para isso você tem que ativar o recurso MARS – Multiple Active Result Sets.

O recurso MARS possibilita abrir múltiplos objetos SqlDataReader em uma única conexão. Ele permite que um aplicativo tenha mais de um SqlDataReader aberto em uma conexão quando cada instância do SqlDataReader é iniciada a partir de um comando separado. Cada objeto SqlCommand que você adicionar acrescenta uma sessão adicional para a conexão.

O recurso MARS está disponível em muitos SGBDs, sendo que o SQL Server 2005 foi o primeiro a dar suporte ao MARS. Você pode controlar explicitamente a ativação do recurso utilizando um par de palavras-chave em sua sequência de conexão. Para ativar o recurso MARS em sua conexão, você define explicitamente o atributo MultipleActiveResultSets na sequência de conexão para True da seguinte forma:

string northwindConnectionString = “Server=localhost;Database=Northwind;Trusted_Connection=True;MultipleActiveResultSets=True”;

Vou mostrar um exemplo prático usando os seguintes recursos:

  • Visual C# 2010 Express Edition
  • SQL Server 2008 Express Edition
  • Banco de dados Northwind.mdf (attached)

Criando o projeto

Neste projeto exemplo, vamos abrir dois DataReader usando dois comandos na mesma conexão:

  • Comando cmdOrder e DataReader drOrder – Obtém os 5 primeiros pedidos da tabela Orders;
  • Comando cmdDetail e DataReader drDetail – Obtém os detalhes do pedido para cada pedido na tabela Order Details.

Vamos criar um novo projeto no Visual C# 2010 Express Edition acionando o menu File -> New Project e a seguir escolhendo o template Windows Forms Application.

Informe o nome MultiplosComandos_PorConexao e clique no botão OK.

A seguir, abra a janela Database Explorer e clique no ícone Data Connections com o botão direito do mouse e selecione Add Connections.

Na janela Add Connection, escolha o Data Source – Microsoft SQL Server Database File (SqlClient) – e informe o local do banco de dados Northwind.mdf.

net_pag1

Obs: Eu estou anexando o banco de dados Northwind.mdf no meu SQL Server.

Após clicar no botão OK, você deverá ter acesso a todos os objetos do banco de dados Northwind.mdf no SQL Server.

No formulário padrão form1.cs, inclua os seguintes controles:

  • ListBox – lstDados
  • Button – btnExecutar e btnEncerrar

Disponha os controles conforme o leiaute da figura a seguir:

adn_mcc1

Vamos declarar os seguintes namespaces no inicio do formulário:

using System;
using System.Windows.Forms;
using System.Data.SqlClient;

Vamos definir a string de conexão e ativar o recurso MARS :

string sqlConnectString = @”Data Source=.\SQLEXPRESS;AttachDbFilename=C:\dados\Northwind.MDF;Integrated Security=True;Connect Timeout=30;User Instance=True;MultipleActiveResultSets=True”

O código do evento Click do botão Executar é mostrado a seguir:

private void btnExecutar_Click(object sender, EventArgs e)
        {
            string sqlConnectString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\dados\Northwind.MDF;Integrated Security=True;Connect Timeout=30;User Instance=True;MultipleActiveResultSets=True";

            SqlConnection connection = new SqlConnection(sqlConnectString);

            // cria o DataReader com 3 registros da tabela Orders
            SqlCommand cmdOrder = connection.CreateCommand();
            cmdOrder.CommandText ="SELECT TOP 5 OrderID, OrderDate,ShipCity FROM Orders";
            connection.Open();
            SqlDataReader drOrder = cmdOrder.ExecuteReader();

            // Percorre o reader com os registros dos pedidos
            while (drOrder.Read())
            {
                lstDados.Items.Add("Pedido ID : " + drOrder["OrderID"] + " Cidade : " + drOrder["ShipCity"]);

                // Cria um DataReader com os detalhes do pedido
                SqlCommand cmdDetail = connection.CreateCommand();
                cmdDetail.CommandText = "SELECT ProductID, Quantity FROM [Order Details] WHERE OrderID=" + drOrder["OrderID"];
                // percorre os detalhes do pedido para o pedido
                using (SqlDataReader drDetail = cmdDetail.ExecuteReader())
                {
                    while (drDetail.Read())
                    {
                        lstDados.Items.Add("\tProduto ID : " + drDetail["ProductID"] + "\tQuantidade : " + drDetail["Quantity"]);
                    }
                    lstDados.Items.Add("-------------------------------------------------------------------");
                    drDetail.Dispose();
                }
            }
            connection.Close();
        }
    }

No código acima, temos o seguinte:

  1. Definimos a string de conexão
  2. Criamos o comando cmdOrder
  3. Abrimos a conexão
  4. Criamos o DataReader drOrder sobre o comando cmdOrder
  5. Percorremos o DataReader drOrder
  6. Exibimos o no. do pedido e a cidade
  7. Criamos o comando cmdDetail
  8. Definimos um novo DataReader drDetail no comando cmdDetail
  9. Percorremos o DataReader drDetail e exibimos os detalhes do pedido

Executando o projeto iremos obter o seguinte resultado:

adn_mcc2

O exemplo mostra que ativando o recurso MARS podemos usar mais de um comando em uma única conexão.

Pegue o projeto completo aqui: MultiplosComandos_PorConexao.zip

O post ADO .NET – Executando múltiplos comandos em uma única conexão apareceu primeiro em .

C# – Apresentando TASKS

$
0
0

A plataforma .NET versão 4.0 apresenta o novo namespace System.Threading.Tasks, que contém classes que permitem abstrair a funcionalidade de threading em que, na verdade, por trás dos panos, uma ThreadPool é usada.

Uma tarefa (ou task) representa uma unidade de trabalho que deverá ser realizada. Essa unidade de trabalho pode rodar em uma thread separada e é também possível iniciar uma task de forma sincronizada que resulta em uma espera pela thread chamada. Com tarefas, você tem uma camada de abstração, mas também um bom controle sobre as threads relacionadas.

As tarefas (tasks) permitem muito mais flexibilidade na organização do trabalho que você precisa fazer. Por exemplo, você pode definir continuar o trabalho, que deve ser feito depois que uma tarefa esteja completa. Isso pode diferenciar se um tarefa foi executada com sucesso ou não. Você também pode organizar as tarefas em hierarquia em que uma tarefa pai pode criar novas tarefas filhas, que podem criar dependências e, assim, o cancelamento da tarefa pai também cancela suas tarefas filhas.

Iniciando Tasks

Para iniciar uma tarefa, você pode usar a classe TaskFactory ou o construtor da classe Task e o método Start(). O construtor Task lhe dá mais flexibilidade na criação da tarefa. Ao iniciar uma tarefa, uma instância da classe Task pode ser criada, e o código que deve ser executado pode ser atribuído com uma Action ou delegate Action<object> tanto sem parâmetro como com um parâmetro object. Isso é semelhante ao que você viu na classe Thread.

No exemplo abaixo, o método é definido sem parâmetro e a ID da tarefa é exibida no Console:

using System.Threading.Tasks;

    static void MetodoTask()
    {
        Console.WriteLine("executando uma tarefa (task)");
        Console.WriteLine("Task id: {0}", Task.CurrentId);
    }

Instâncias de tarefas podem ser criadas de várias maneiras. A abordagem mais comum é utilizar o tipo de propriedade Factory do tipo Task para recuperar uma instância TaskFactory que pode ser usada para criar tarefas para vários propósitos. Por exemplo, para criar uma tarefa que executa uma ação, o método factory StartNew pode ser usado:

using System;
using System.Threading.Tasks;

namespace Usando_Task
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = Task.Factory.StartNew(() => FazerAlgo());
            Console.ReadKey();
        }
        static void FazerAlgo()
        {
            Console.WriteLine("executando uma tarefa => FazerAlgo() (task)");
        }
    }
}

Existem maneiras diferentes para iniciar uma nova tarefa.

A primeira maneira é instanciando uma classe TaskFactory, em que o método MetodoTarefa é passado para o método StartNew(), e a tarefa é iniciada imediatamente:

// usando factory task
  TaskFactory tf = new TaskFactory();
  Task t1 = tf.StartNew(MetodoTarefa); 	// usando a factor task via task Task t2 = Task.Factory.StartNew(MetodoTarefa);

A segunda abordagem usa o construtor da classe de Task. Quando o objeto Task é instanciado, a tarefa não será executada imediatamente. Em vez disso, a ela é dado o status Created. A tarefa é, então, iniciada pela chamada do método Start() da classe Task.

// usando o construtor Task
Task t3 = new Task(MetodoTarefa);
t3.Start();

Com a classe de Task, em vez de invocar o método Start(), você pode invocar o método RunSynchronously(). Dessa forma, a tarefa é iniciada também, mas ela está sendo executada na thread atual do chamador, que precisa esperar até que a tarefa termine. Por padrão, a tarefa é executada de forma assíncrona.

A classe Task também fornece construtores que inicializam a tarefa, mas que não a agendam para execução. Por razões de desempenho, o método StartNew da classe TaskFactory deve ser o mecanismo preferido para criação e programação de tarefas, mas, para situações em que a criação e a programação devem ser separadas, os construtores podem ser usados, e o método Start() da tarefa pode então ser utilizado para programar a tarefa para execução em um momento posterior.

Para as operações que retornam valores, a classe Task<TResult> deve ser usada.

Continuando Tarefas

Usando a classe Tasks você pode especificar que, depois que uma tarefa for concluída, outra tarefa específica deve começar a ser executada – por exemplo, uma nova tarefa que usa um resultado da anterior ou que deve fazer uma limpeza se a tarefa anterior falhou. Considerando que o manipulador tarefa ou não tem parâmetro ou tem um parâmetro object, o manipulador de continuação tem um parâmetro do tipo Task. Aqui, você pode acessar informações sobre a tarefa de origem.

É muito comum para uma operação assíncrona, na conclusão, invocar uma segunda operação e passar os dados para ela.

Tradicionalmente, isso tem sido feito por meio de métodos de retorno. Na biblioteca Task Parallel, a mesma funcionalidade é fornecida por tarefas de continuação. Uma tarefa de continuação (também conhecida como uma continuação) é uma tarefa assíncrona que é invocada por outra tarefa, o que é conhecido como a antecedente, quando a antecedente termina.

As Continuações são relativamente fáceis de utilizar, e são muito eficientes e flexíveis. Por exemplo, você pode:

  • passar dados da antecedente para a continuação;
  • especificar as condições precisas em que a continuação será ou não invocada;
  • cancelar uma continuação ou antes de começar ou cooperativamente quando ela estiver sendo executada;
  • fornecer dicas sobre como a continuação deve ser agendada;
  • invocar múltiplas continuações da mesma antecedente;
  • invocar uma continuação quando todas ou qualquer uma das múltiplas antecedentes completarem;
  • vincular continuações uma após a outra a qualquer comprimento arbitrário;
  • usar uma continuação para manipular exceções lançadas pela antecedente.

Podemos criar continuações usando o método Task.ContinueWith. O exemplo a seguir mostra o padrão básico, (por motivos de clareza, o tratamento de exceção é omitido).

using System;
using System.Threading.Tasks;

namespace Continuation_Tasks
{
    class Program
    {
        static void Main(string[] args)
        {
            // A tarefa antecedente. Pode tambem ser criada com Task.Factory.StartNew.
            Task<DayOfWeek> tarefaA = new Task<DayOfWeek>(() => DateTime.Today.DayOfWeek);

            // A continuacao. Seu delegate toma a tarefa antecedente
            // como um argumento e pode retornar um tipo diferente
            Task<string> continuacao = tarefaA.ContinueWith((antecedent) =>
            {
                return String.Format("Hoje é {0}.",antecedent.Result);
            });

            // Iniciar a antecedente
            tarefaA.Start();

            // Usar o resultada da continuacao
            Console.WriteLine(continuacao.Result);
            Console.ReadKey();
        }
    }
}

 

c_task2

Também é possível criar uma continuação multitarefa que será executada quando qualquer uma ou todas as tarefas de um array de tarefas tiverem sido completadas, como mostrado a seguir:

using System;
using System.Threading.Tasks;

namespace Continuation_Tasks2
{
    class Program
    {
        static void Main(string[] args)
        {
            Task<int>[] tarefas = new Task<int>[2];
            tarefas[0] = new Task<int>(() =>
            {
                // faz alguma coisa... 
                return 34;
            });

            tarefas[1] = new Task<int>(() =>
            {
                // faz alguma coisa... 
                return 8;
            });

            var continuation = Task.Factory.ContinueWhenAll(
                            tarefas,
                            (antecedents) =>
                            {
                                int resposta = tarefas[0].Result + tarefas[1].Result;
                                Console.WriteLine("A resposta é {0}", resposta);          
                            });

            tarefas[0].Start();
            tarefas[1].Start();
            continuation.Wait();
            Console.ReadKey();
        }
    }
}

 

c_task1

 

O método Task.WhenAll aguarda assincronamente múltiplas operações assíncronas que são representadas através de uma coleção de tarefas. Usamos o método Task.WhenAll em um conjunto de tarefas. A aplicação de WhenAll retorna uma única tarefa que não está completa até que cada tarefa na coleção seja concluída. As tarefas parecem ser executadas em paralelo, mas não são criadas novas threads.

Uma continuação é criada no estado WaitingForActivation e, portanto, só pode ser iniciada por sua tarefa antecedente. Chamar Task.Start em uma continuação no código do usuário levanta uma exceção System.InvalidOperationException.

A continuação é por si só uma tarefa e não bloqueia a thread na qual ela é iniciada. Use o método Wait para bloquear até a tarefa da continuação terminar.

A classe Task dá suporte para cancelamento cooperativo e é totalmente integrada com a classe System.Threading.CancellationTokenSource e com a classe System.Threading.CancellationToken, que são novas no Framework 4. NET.

Muitos dos construtores da classe System.Threading.Tasks.Task tomam um CancellationToken como parâmetro de entrada. Muitas das sobrecargas StartNew e Run também possuem um CancellationToken.

O post C# – Apresentando TASKS apareceu primeiro em .

C# – Capturando e tratando Exceptions (revisão)

$
0
0

As exceções (Exceptions) são mecanismos primários para comunicar condições de erros. Elas possuem um grande poder e isso traz também grandes responsabilidades. Dessa forma, não devemos abusar deste recurso, mas saber usá-lo com bom senso.

Vou apresentar um resumo bem objetivo de como usar o recurso das exceções acrescentando assim mais esse artigo aos que já escrevi o sobre o assunto.

1. Lançando uma Exception

Quando você precisar indicar que ocorreu um erro em uma determinada situação e que não pode ser tratado no nível de código atual, você pode lançar a exceção para ser tratada em outro nível usando a declaração throw. Essa declaração é usada para sinalizar a ocorrência de uma situação anormal (exceção) durante a execução do programa. A exceção gerada é um objeto cuja classe é derivada de System.Exception.

private void Teste(string valor)
{
if (string.IsNullOrEmpty(value))
{
 throw new ArgumentNullException(“valor”, “o valor do parâmetro não pode ser null”);
}
...
}

Você pode lançar explicitamente uma exceção usando a instrução throw. Você também pode lançar uma exceção capturada novamente usando a instrução throw. É uma boa prática de codificação adicionar informações a uma exceção que é relançada para fornecer mais informações quando da depuração.

Geralmente a instrução throw é usada com um bloco try/catch/finally.

O exemplo a seguir usa um bloco try/catch para capturar uma exceção do tipo FileNotFoundException.

Após o bloco try, há o bloco catch que captura a FileNotFoundException e grava uma mensagem para o console, se o arquivo de dados não foi encontrado. A declaração seguinte é a instrução throw que lança um FileNotFoundException nova e adiciona informações de texto para a exceção.

public static void Main()
 {
 FileStream fs = null;
 try
 {
 //Abre o arquivo texto
 fs = new FileStream(@"C:\teste\dados.txt", FileMode.Open);
 StreamReader sr = new StreamReader(fs);
 string line;

//Um valor é lido do arquivo e exibido no console
 line = sr.ReadLine();
 Console.WriteLine(line);
 }
 catch(FileNotFoundException e)
 {
 Console.WriteLine("[Arquivo de dados não existe] {0}", e);
 throw new FileNotFoundException(@"[Arquivo dados.txt não existe na pasta c:\teste]",e);
 }
 finally
 {
 if (fs != null)
 fs.Close();
 }
 }

De forma prática, você deve lançar uma exceção quando uma das seguintes condições ocorrer:

  • O método não conseguiu completar a sua funcionalidade definida

if (original == null)

 {
 throw new System.ArgumentException("Parâmetro não pode ser null", "original");
 }
  • Uma chamada inadequada a um objeto é feita, com base no estado do objeto
void WriteLog()
 {
 if (!this.logFile.CanWrite)
 {
 throw new System.InvalidOperationException("O arquivo Logfile não pode ser somente leitura");
 }
 }
  • Quando um argumento para um método faz com que ocorra uma exceção
try
 {
 return array[index];
 }
 catch (System.IndexOutOfRangeException ex)
 {
 System.ArgumentException argEx = new System.ArgumentException("Indice fora do intervalo", "index", ex);
 throw argEx;
 }

Exceções contêm uma propriedade chamada StackTrace. Esta string contém o nome dos métodos na pilha de chamada atual, juntamente com o nome do arquivo e o número da linha onde a exceção foi lançada para cada método.

Um objeto StackTrace é criado automaticamente pelo Common Language Runtime (CLR) do ponto da instrução throw, de modo que as exceções devem ser lançados a partir do ponto onde o rastreamento de pilha deve começar.

Todas as exceções contém uma propriedade chamada Message.

Essa string deve ser definida para explicar a razão para a exceção. Note que a informação que é sensível à segurança não deve ser colocada no texto da mensagem. Além de Message, a ArgumentException contém uma propriedade chamada ParamName, que deve ser definida para o nome do argumento que causou a exceção a ser lançada. No caso de uma propriedadesetter, ParamName deve ser definido como value.

Métodos Public e Protected devem lançar exceções sempre que eles não puderem concluir suas funções pretendidas. A classe execption que é lançada deve ser a exceção mais específica disponível que se adapta às condições de erro. Essas exceções devem ser documentadas como parte da funcionalidade da classe, e as classes derivadas ou atualizações para a classe original devem manter o mesmo comportamento para compatibilidade com versões anteriores.

O que você deve evitar quando lançar exceções:

  • Exceções não devem ser usadas para alterar o fluxo de um programa como parte da execução normal. Exceções só devem ser usadas para relatar e lidar com condições de erro;
  • Exceções não devem ser retornadas como um valor de retorno ou parâmetro ao invés de serem lançadas;
  • Não lance System.Exception, System.SystemException, System.NullReferenceException, ou System.IndexOutOfRangeException intencionalmente a partir do seu próprio código fonte;
  • Não crie exceções que podem ser lançadas em modo de depuração, mas não em modo de release (liberação). Para identificar erros em tempo de execução (run-time) durante a fase de desenvolvimento, use Debug Assert;

2. Capturando Exceções

Quando você precisar tratar exceções que foram lançadas, você deve envolver o código que pode potencialmente lançar uma exceção no interior de um bloco try{ } seguido por um bloco catch { }:

try
{
 FazAlgumaCoisa(null);
}
catch (ArgumentNullException ex)
{
 Console.WriteLine(“Exception: “ + ex.Message);
}

A instrução try-catch consiste em um bloco try seguido por uma ou mais cláusulas catch, que especifica manipuladores para exceções diferentes. Quando uma exceção é lançada, oCommon Language Runtime (CLR) procura a instrução catch que trata essa exceção.

Se o método atualmente em execução não contiver um bloco catch, o CLR olha para o método que chamou o método atual, e assim por diante na pilha de chamadas. Se não for encontrado nenhum bloco catch, o CLR exibe uma mensagem de exceção sem tratamento para usuário e interrompe a execução do programa.

O bloco de try contém o código protegido que pode causar a exceção. O bloco é executado até que uma exceção seja gerada ou ele seja concluído com sucesso. Por exemplo, a seguinte tentativa de converter um objeto null gera a exceção NullReferenceException:

object o2 = null;
try
{
 int i2 = (int)o2; // Erro
}

Embora a cláusula catch possa ser usada sem argumentos para capturar qualquer tipo de exceção, esse uso não é recomendado. Em geral, você só deve capturar essas exceções que você sabe como recuperar. Portanto, você sempre deve especificar um argumento de objeto derivado de System.Exception, por exemplo:

catch (InvalidCastException e)
{
}

É possível usar mais de uma cláusula catch específica na mesma instrução try-catch. Neste caso, a ordem das cláusulas catch é importante porque elas são examinadas em ordem.

Capture as exceções mais específicas antes das menos específicas.

O compilador irá gerar um erro se você ordenar seus blocos catch de forma que um bloco catch posterior nunca seja alcançado.

try
{
 throw new ArgumentNullException();
}
catch (ArgumentException ex)
{
 //será alcançada aqui
}
catch (ArgumentNullException ex)
{
 //não será alcançada
}

Como ArgumentNullException é um tipo de ArgumentException, e ArgumentException é a primeira na lista de captura, ela será chamada primeiro, de forma que ArgumentNullException nunca será alcançada.

Corrigindo a ordem temos:

try
{
 throw new ArgumentNullException();
}
catch (ArgumentNullException ex)
{
 //será alcançada aqui
}
catch (ArgumentException ex)
{
 //captura qualquer outra ArgumentException ou filha
}

3. Relançando uma Exception

Se você precisar lidar com uma exceção em um nível (registrar um log, por exemplo) e em seguida, repassar a exceção para um nível superior de código para ser tratada, você deve relançar a exceção.

Existem duas maneiras de resolver este problema. A solução mais simples e ingênua , que geralmente está incorreta, é esta:

try
{
 DoSomething();
}
catch (ArgumentNullException ex)
{
 LogException(ex);
 // a forma incorreta de relançar uma exceção
 throw ex; //relança a exceção para um nível superior
}

O que há de tão errado com isso?

Sempre que uma exceção é lançada, a localização atual da pilha é salva para a exceção. Quando você relançar uma exceção da forma mostrada acima você substitui a localização da pilha que estava originalmente na exceção com a que foi capturada neste bloco catch e provavelmente não é isso o que você queria fazer.

Se você quer relançar uma exceção preservando a chamada original da pilha, lance a exceção sem usar a variável exception.

A seguir temos a forma correta:

try
{
 DoSomething();
}
catch (ArgumentNullException ex)
{
 LogException(ex);
 // a forma correta
 throw; //relança a exceção para um nível superior e preserva a pilha
}

Fique esperto quando interceptar uma exceção.

Você não vai querer logar uma exceção e então relançá-la para múltiplos níveis, fazendo com que o mesmo erro seja logado mais de uma vez .

Muitas vezes, o registro deve ser tratado somente no nível mais elevado. Além disso, cuidado com a armadilha de fazer o tratamento de exceções no nível mais baixo. Se você não pode dar um tratamento inteligente para a exceção, basta deixar um nível mais elevado se preocupar com o seu tratamento.

4. Garantindo a execução com Finally

Você quer garantir que os recursos usados sejam liberados, mesmo quando ocorrerem exceções.

Frequentemente, quando você usar objetos que encapsulam recursos externos (como conexões de banco de dados ou arquivos), vai querer garantir que os recursos sejam liberados quando o trabalho for terminado. E vai querer garantir o mesmo quando ocorrerem exceções. No entanto, se uma exceção for lançada enquanto você estiver usando os recursos, a exceção normalmente irá ignorar qualquer código para liberar os recursos você tenha escrito.

Use o bloco finally que garante que o código definido no bloco sempre será executado mesmo ocorrendo uma exceção.

O bloco finally é útil para limpar todos os recursos que foram alocados em um bloco try. O controle sempre será passado para o bloco finally e o código definido no bloco sempre será executado.

Normalmente, as declarações de um finally bloco são executadas quando o controle sai de um bloco try. Se a transferência de controle ocorre como resultado da execução normal, da execução de um break, continue, goto, ou return de instrução, ou de propagação de uma exceção da try instrução.

StreamWriter stream = null;
try
{
 stream = File.CreateText(“temp.txt”);
 stream.Write(null, -1, 1);
}
catch (ArgumentNullException ex)
{
 Console.WriteLine(“No catch: “);
 Console.WriteLine(ex.Message);
}
finally
{
 Console.WriteLine(“No finally: Fechando o arquivo”);
 if (stream != null)
 {
 stream.Close();
 }
}

Um bloco catch não é necessário quando um bloco finally for usado.

Existe uma situação quando o bloco finally não é executado: se o seu código forçar uma saída imediata usando Exit() o bloco finally não será executado.

try
{
//faz algo
Environment.Exit(1) // o programa encerra AGORA
}
finally
{
 //este código nunca será executado
}

5. Obtendo informações importantes de uma exceção

Como obter informações importantes de uma exceção?

As exceções são objetos ricos em informação. A tabela abaixo lista as propriedades/métodos disponíveis para todos os tipos de exceções:

Propriedade/Método Descrição
ToString() Imprime o tipo de exceção, seguido da mensagem e StackTrace.
Message Uma breve descrição do erro.
Source A aplicação onde a exceção ocorreu.
StackTrace A lista dos métodos na pilha atual. Útil para rastrear o caminho que causou a exceção.
TargetSite O método que lançou a exceção
InnerException A exceção que causou a exceção atual. Muitas vezes, as exceções são envolvidas no interior de outras exceções de nível superior.
HelpLink Um link para um arquivo de ajuda, muitas vezes na forma de uma URL.
Data Pares chave-valor de exceções especificas que fornecem mais informações.
Exemplo de uso:
using System;
using System.Collections;

namespace Excecoes
{
 class Program
 {
 static void Main(string[] args)
 {
 try
 {
 int divisor = 0;
 Console.WriteLine("{0}", 13 / divisor);

 }
 catch (DivideByZeroException ex)
 {
 Console.WriteLine("ToString() : " + ex.ToString());
 Console.WriteLine("Message : " + ex.Message);
 Console.WriteLine("Source : " + ex.Source);
 Console.WriteLine("HelpLink : " + ex.HelpLink);
 Console.WriteLine("TargetSite : " + ex.TargetSite);
 Console.WriteLine("Inner Exception: " + ex.InnerException);
 Console.WriteLine("Stack Trace : " + ex.StackTrace);
 Console.WriteLine("Data : ");
 if (ex.Data != null)
 {
 foreach (DictionaryEntry de in ex.Data)
 {
 Console.WriteLine("\t{0}: {1}", de.Key, de.Value);
 }
 }
 Console.ReadKey();
 }
 }
 }
}

c_excep1

6. Capturando exceções não tratadas

Como obter um manipulador personalizado para todos e quaisquer exceções lançadas por seu aplicativo que não são tratados pelo mecanismo normal do bloco try…catch mecanismo.

Uma exceção lançada é passada para chamada da pilha até encontrar um bloco que a trate. Se nenhum tratamento for possível, o processo irá encerrar a aplicação.

Felizmente, existe uma maneira de capturar as exceções não tratadas e executar o seu próprio código antes da aplicação terminar (ou até mesmo impedi-la de terminar). A maneira de fazer isso depende do tipo de aplicação. A seguir as possibilidades:

  • Capturando exceções não tratadas em uma aplicação console

Em programas console, você pode ouvir o UnhandledException para a AppDomain atual:

using System;

namespace Excecoes
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
throw new InvalidOperationException("Deu pau...");
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine("Capturando a exceção não tratada");
Console.WriteLine(e.ExceptionObject.ToString());
}
}
}

c_excep2

  • Capturando exceções não tratadas em uma aplicação Windows Forms

No Windows Forms, antes de qualquer outro código ser executado, você deve dizer ao objeto Application que pretende lidar com as exceções não detectadas. Então, você pode ouvir uma ThreadException no segmento principal.

Crie uma aplicação Windows forms que contenha apenas um controle Button e declare no arquivo Program.cs:

using System;
using System.Windows.Forms;

namespace WinFormsExceptions
{
 static class Program
 {
 /// <summary>
 /// The main entry point for the application.
 /// </summary>
 [STAThread]
 static void Main()
 {
 //Este código deve ser executado antes de criar qualquer elemento da UI
 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
 Application.EnableVisualStyles();
 Application.SetCompatibleTextRenderingDefault(false);
 Application.Run(new Form1());
 }
 }
}

No formulário form1.cs declare o seguinte código:

using System.Text;
using System.Windows.Forms;

namespace WinFormsExceptions
{
 public partial class Form1 : Form
 {
 public Form1()
 {
 InitializeComponent();
 //trata qualquer exceção que ocorra nesta thread
 Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
 }

void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
 {
 StringBuilder sb = new StringBuilder();
 sb.AppendLine("Trapped unhandled exception");
 sb.AppendLine(e.Exception.ToString());
 MessageBox.Show(sb.ToString());
 }

private void button1_Click(object sender, System.EventArgs e)
 {
 throw new System.InvalidOperationException("Deu Pau...");
 }
 }
}
  • Capturando exceções não tratadas em uma aplicação WPF

Em uma aplicação WPF escutamos exceções não tratadas no dispatcher:

Crie uma aplicação do tipo WPF Application com um controle Button e defina o seguinte código no arquivo MainWindow.xaml.cs:

using System.Text;
using System.Windows;

namespace Wpf_Execptions
{
 /// <summary>
 /// Interaction logic for MainWindow.xaml
 /// </summary>
 public partial class MainWindow : Window
 {
 public MainWindow()
 {
 InitializeComponent();
 Application.Current.DispatcherUnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(Current_DispatcherUnhandledException);
 }

 void Current_DispatcherUnhandledException(object sender,System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
 {
 StringBuilder sb = new StringBuilder();
 sb.AppendLine("Caught unhandled exception");
 sb.AppendLine(e.Exception.ToString());
 MessageBox.Show(sb.ToString());
 e.Handled = true;//prevent exit
 }

private void button1_Click_1(object sender, RoutedEventArgs e)
 {
 throw new InvalidOperationException("Deu pau...");
 }
 }
}
  • Capturando exceções não tratadas em uma aplicação ASP .NET

Em aplicações ASP.NET, você pode capturar exceções não tratadas quer a nível de página ou a nível de aplicação.

Para pegar erros a nível da página, procure no evento da página de erro. Para capturar erros a nível de aplicação, você deve ter uma classe de aplicativo global (muitas vezes no Global.asax) e colocar o seu comportamento em Application_Error, como abaixo:

public class Global : System.Web.HttpApplication
{
...
protected void Application_Error(object sender, EventArgs e)
{
 //envia para uma aplicação com tratamento de erro
 Server.Transfer(“ErrorHandlerPage.aspx”);
}
...
}

Na página:

public partial class _Default : System.Web.UI.Page
{
 protected void Page_Load(object sender, EventArgs e)
 {
 Button1.Click += new EventHandler(Button1_Click);
 //descomente para tratar o erro a nível de pagina
 //this.Error += new EventHandler(_Default_Error);
 }
 void Button1_Click(object sender, EventArgs e)
 {
 throw new ArgumentException();
 }
}

Com essa pequena compilação sobre exceções procurei dar uma visão geral sobre o tratamento e os aspectos mais importantes sobre esse importante tópico.

O post C# – Capturando e tratando Exceptions (revisão) apareceu primeiro em .

C# – Conheça a sobrecarga de operadores, ou operator overloading

$
0
0

Sobrecarga (overloading) de operadores. O que vem a ser isso?

Se você esta vindo de uma linguagem procedural como Clipper, Dbase , Basic, Cobol, etc, ou se está começando agora, tendo o C# como sua primeira linguagem de programação, pode estranhar o termo. Mas é apenas uma questão de prática.

A sobrecarga (Overload)  é a habilidade de poder definir diversas propriedades, métodos ou procedimentos em uma classe com o mesmo nome, mas parâmetros diferentes.

Você entendeu? Bom, vou ser mais claro… Se você criar dois procedimentos em seu aplicativo com o mesmo nome e com parâmetros diferentes estará usando sobrecarga.

Você deve estar se perguntando: “como posso criar dois procedimentos com o mesmo nome? Como vou diferenciá-los?”. Você vai diferenciar os métodos/procedimentos pela lista de parâmetros que eles vão possuir, isto é , os métodos/procedimentos terão parâmetros diferentes. Vou dar um exemplo:

Suponha que você precise criar uma classe com um método que retorne o valor da área de um quadrado, só que este método vai poder aceitar como parâmetro uma string ou um número:


public double CalculaArea(double lado)
{
 return lado * lado;
}

 


public double CalculaArea(string lado)
{
double ld = 0;
ld = Convert.ToDouble(lado);
return ld * ld;
}

Aqui temos dois métodos com o mesmo nome (CalculaArea), mas que possuem parâmetros diferentes (tipo de dados diferentes) que fornecem o mesmo resultado. Pois bem, podemos fazer a mesma coisa para operadores na linguagem C#.

A linguagem C#, assim como muitas linguagens orientadas a objetos, permitem a sobrecarga de operadores, ou seja a capacidade de redefinir os operadores de uma classe.

Nem todos os operadores podem ser sobrecarregados, e existem restrições relativas a quando certos operadores podem ser sobrecarregados, como a definição dos operadores == e /=.

Através da sobrecarga de operadores podemos realizar operações complexas, através de argumentos de funções, e isso nos permite escrever um código muito mais intuitivo e legível.

Para modificar um operador na linguagem C# usamos a seguinte sintaxe:

[modificadores*][type] operator [operator] (parâmetros){(…)}

Os operadores uniários recebem somente um parâmetro e os operadores binários recebem dois parâmetros, sendo que em cada caso um dos parâmetros deve ser do tipo da classe que esta redefinindo o operador.

Operador uniário:


public static ret-type operator op(parameter-type operand)
{
}

Operador binário:


public static ret-type operator op(parameter-type operand1, parameter-type operand1)
{
}

Obs: Os operadores redefinidos devem ser public static.

Vejamos um exemplo prático onde iremos realizar a sobrecarga do operador ++: abra o Visual C# 2010 Express Edition e crie um novo projeto do tipo Console Application com o nome SobreCargaOperadores.

A seguir, defina o seguinte código na classe Program.cs:


using System;

namespace SobrecargarOperadores
{
 class Program
 {
 static void Main(string[] args)
 {
 Calculo i = new Calculo(100, 200, 300);
 Calculo j = new Calculo(5, 10, 3);
 i++;
 i.MostraResultado();
 j++;
 j.MostraResultado();
 Console.WriteLine();
 }

 }
 class Calculo
 {
 int a, b, c;
 public Calculo()
 {
 a = b = c = 0;
 }
 public Calculo(int x, int y, int z)
 {
 a = x;
 b = y;
 c = z;
 }
 public static Calculo operator ++(Calculo op1)
 {
 op1.a++;
 op1.b++;
 op1.c++;
 return op1;
 }
 public void MostraResultado()
 {
 Console.WriteLine(a + "," + b + "," + c);
 Console.ReadLine();
 }
 }
}

Aqui criamos a classe Calculo e redefinimos o operador ++ para realizar o incremento em uma unidade dos argumentos recebidos.

Executando o projeto teremos:

c_poli12

Muito simples, não é mesmo?! Vamos agora realizar a sobrecarga de operadores binários para os operadores + , – , / e * (adição, subtração, divisão e multiplicação). 

No menu File clique em Add -> Project e a seguir informe o nome OperadoresBInarios;
Defina a seguir o código abaixo na classe Program.cs:


using System;

namespace OperadoresBinarios
{
 class Program
 {
 static void Main(string[] args)
 {
 Calculo i = new Calculo(10, 20, 30);
 Calculo j = new Calculo(5, 10, 15);
 Calculo k = new Calculo();
 Console.WriteLine("Sobrecarga de Operadores");
 Console.WriteLine("i = 10, 20, 30");
 Console.WriteLine("j = 5, 10, 15");
 Console.WriteLine();
 //adição
 k = i + j;
 Console.WriteLine("+");
 k.MostraResultado();
 Console.WriteLine();
 //subtração
 k = i - j;
 Console.WriteLine("-");
 k.MostraResultado();
 Console.WriteLine();
 //multiplicação
 k = i * j;
 Console.WriteLine("*");
 k.MostraResultado();
 Console.WriteLine();
 //divisão
 k = i / j;
 Console.WriteLine("/");
 k.MostraResultado();
 Console.WriteLine();
 }
 }
 class Calculo
 {
 int a, b, c;
 public Calculo()
 {
 a = b = c = 0;
 }
 public Calculo(int x, int y, int z)
 {
 a = x;
 b = y;
 c = z;
 }
 public static Calculo operator +(Calculo op1, Calculo op2)
 {
 Calculo calc = new Calculo();
 calc.a = op1.a + op2.a;
 calc.b = op1.b + op2.b;
 calc.c = op1.c + op2.c;
 return calc;
 }
 public static Calculo operator -(Calculo op1, Calculo op2)
 {
 Calculo calc = new Calculo();
 calc.a = op1.a - op2.a;
 calc.b = op1.b - op2.b;
 calc.c = op1.c - op2.c;
 return calc;
 }
 public static Calculo operator *(Calculo op1, Calculo op2)
 {
 Calculo calc = new Calculo();
 calc.a = op1.a * op2.a;
 calc.b = op1.b * op2.b;
 calc.c = op1.c * op2.c;
 return calc;
 }
 public static Calculo operator /(Calculo op1, Calculo op2)
 {
 Calculo calc = new Calculo();
 calc.a = op1.a / op2.a;
 calc.b = op1.b / op2.b;
 calc.c = op1.c / op2.c;
 return calc;
 }
 public void MostraResultado()
 {
 Console.WriteLine(a + "," + b + "," + c);
 Console.ReadLine();
 }
 }
}

Aqui temos a sobrecarga dos operadores +, -, / e *.

Executando o projeto teremos:

c_poli13

Se os operadores de comparação (==) forem sobrecarregados, essa sobrecarga deve ser feita aos pares; ou seja, se for sobrecarregado o operador == também se deve sobrecarregar o operador !=. O mesmo vale para os operadores < e >, e <= e >=.

Outro fato importante é que se, por exemplo, você está sobrecarregando o operador ==, não pode usar (a == b), (a == null) ou (b == null) para verificar a igualdade de referência dentro da sobrecarga.

Isso resulta em uma chamada para o operador sobrecarregado ==, que é o mesmo que voltar a chamar o operador que você está redefinindo e como resultado há um loop infinito. UseReferenceEquals ou converta o tipo de objeto para evitar isso.

Pegue o projeto completo aqui: SobrecargarOperadores.zip

O post C# – Conheça a sobrecarga de operadores, ou operator overloading apareceu primeiro em .

ASP .NET 4.0 – Aplicando estilos com CSS em um GridView

$
0
0

Para demonstrar como aplicar estilos a um Gridview, vamos criar um site simples, acessar um banco de dados e preencher um controle gridview com os dados. A seguir, teremos aplicar um estilo ao controle GridView usando um arquivo CSS (Cascading Style Sheet).

Abra o Microsoft Visual Studio Express 2012 For Web e no menu File clique em New WebSite. A seguir, selecione a linguagem Visual Basic, o template ASP .NET Empty Web Site e informe o nome GridView_CSS. Ao final, clique em OK.

aspn_gdvss1

Logo após, faça o seguinte:

  • Clique com o botão direito sobre o projeto e selecione Add ASP .NET Folder;
  • Selecione o item App_Data;
  • Clique com o botão direito do mouse sobre a pasta App_Data e clique em Add Existing Item;
  • Selecione o banco de dados Cadastro.mdf e clique em Add.

Com isso incluímos o banco de dados Northwind.mdf em nosso site. Na janela Solution Explorer veremos a seguinte estrutura:

aspn_gdvss2

Vamos usar a tabela Products do banco de dados Northwind.mdf.

Agora vamos incluir um formulário web no projeto:

  • Clique com o botão direito do mouse no projeto;
  • Selecione Add New Item e a seguir selecione o template Web Form e aceite o nome Default.aspx e clique em Add.

aspn_gdvss2

  • Abra o arquivo Default.aspx no modo Design e arraste a partir da ToolBox um controle GridView no web form;
  • Expanda o menu GridView Tasks e selecione a opção <new data source…> em Choose Data Source:

aspn_gdvss4

  • Selecione a opção SQL DataBase e clique em OK:

aspn_gdvss5

  • Selecione a conexão com o Northwind.mdf e clique em Next>:

aspn_gdvss6

  • Marque a opção para salvar a conexão e clique em Next>;
  • Selecione a tabela Clientes e clique em Next> e a seguir em Finish.
    No menu Project, clique em Add New Item e a seguir selecione o template Style Sheet e aceite o nome StyleSheet.css:

aspn_gdvss7

A seguir, defina o código abaixo no arquivo de estilo que iremos usar:

aspn_gdvss8

O controle GridView gera uma tabela na qual exibe seus dados. A fim de aplicar um estilo no gridview, devemos aplicar uma classe ao grid para que os efeitos das células da tabela que realmente afetar. Iniciamos com o cabeçalho da tabela e alteramos o tamanho da fonte, peso e cor na tag ’th’. Adicionamos uma borda simples e alguns estofamentos. Em seguida, aplicamos o estilo na tag ’td’ adicionando em algum estofamento e em uma borda simples.

Aplicando o estilo

Abra o arquivo Default.aspx no modo Source e, a seguir, selecione e arraste o arquivo de estilo StyleSheet.css no interior da tag head para referenciar o arquivo css:

aspn_gdvss9

Agora, defina a propriedade CssClass do controle GridView para gridview:

aspn_gdvssa

Pronto!

Executando o projeto iremos ver o estilo aplicado ao GridView conforme abaixo:

aspn_gdvssb

O exemplo aplica um estilo bem simples, mas fique a vontade para incrementá-lo!

Pegue o projeto completo aqui: GridView_CSS.zip

O post ASP .NET 4.0 – Aplicando estilos com CSS em um GridView apareceu primeiro em .


.NET – MongoDB – Executando consultas com LINQ

$
0
0

Em meu artigo “.NET – Apresentando e usando o MongoDB“ eu dei uma visão geral sobre o MongoDB, sua instalação e utilização na plataforma .NET. Hoje eu mostro como podemos executar consultas LINQ com o MongoDB. Então, vamos lá!

Recursos usados neste artigo:

Com o MongoDB e o driver MongoDB C# Driver (server também para VB .NET) devidamente instalados, vamos criar um projeto desta vez usando a linguagem C# e mostrar com executar consultas LINQ.

Lembre-se de executar o MongoDB para atender as requisições.

Iniciar o MongoDB manualmente

Navegue até a pasta onde você instalou o MongoDB e abra o diretório bin, depois execute o arquivo mongod.exe. Abaixo vemos isso sendo feito via prompt de comando:

net_mongo3

Usando o MongoDB em um projeto C# com consultas LINQ

Abra o Visual Studio Express 2012 for desktop e no menu FILE clique em New Project. Selecione o template C# -> Windows -> Windows Forms Application e informe nome MongoDB_Linq e clique no botão OK.

net_mglq1

Clique com o botão direito sobre o nome do projeto e selecione Add Reference. Depois clique em Browse e localize a pasta onde você instalou o driver C# do MongoDB. Selecione, a seguir, os arquivos MongoDB.Boson.dll e MongoDB.Driver.dll e clique em Add:

net_mongo9

Clique com o botão direito sobre o nome do projeto e selecione Add Reference. Vamos incluir uma referência ao namespace System.Configuration clicando na guia Assemblies e marcando a opção conforme abaixo:

net_mglq3

Abra o arquivo App.Config e inclua a definição da string de conexão usada para o MongoDB. Na minha máquina local a conexão está sendo feita em localhost porta 27017:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <appSettings>
    <add key="stringConexaoMongoDB" value="Server=localhost:27017"/>
  </appSettings>
</configuration>

No menu PROJECT clique em Add Class e informe o nome Contatos.cs. A seguir, defina o código da classe Contato conforme abaixo:

using MongoDB.Bson;

namespace MongoDB_Linq
{
    class Contato
    {
        public ObjectId Id { get; set; }
        public string Nome { get; set; }
        public string Email { get; set; }
        public int Idade { get; set; }
    }
}

No menu PROJECT clique em Add Class e informe o nome AcessoMongo.cs. Esta classe será responsável pelo acesso ao MongoDB.

A seguir defina o código da classe conforme abaixo:

using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System.Configuration;

namespace MongoDB_Linq
{

    public class AcessoMongo
    {
        static Contato _contato;

        public static string ConnectionString
        {
            get
            {
                return ConfigurationManager.AppSettings["stringConexaoMongoDB"];
            }
        }

        public void CriarDados()
        {
            CriarContatos();
        }

        public void CriarContato(string nome, string email, int idade)
        {
            MongoClient cliente = new MongoClient(ConnectionString);
            MongoServer server = cliente.GetServer();
            MongoDatabase database = server.GetDatabase("MongoLinq");

            var colecao = database.GetCollection<Contato>("Contatos");

            BsonDocument contato = new BsonDocument {
                        { "Nome", nome },
                        { "Email", email },
                        { "Idade", idade }
                        };

            colecao.Insert(contato);
        }

        private static void CriarContatos()
        {
            try
            {
                MongoClient cliente = new MongoClient(ConnectionString);
                MongoServer server = cliente.GetServer();
                MongoDatabase database = server.GetDatabase("MongoLinq");

                var colecao = database.GetCollection<Contato>("Contatos");

                _contato = new Contato
                {
                    Nome = "Jose Carlos",  Email = "macoratti@yahoo.com", Idade = 45
                };
                colecao.Insert(_contato);
                _contato = new Contato
                {
                    Nome = "Miriam", Email = "miriamsq@bol.com.br", Idade = 38
                };
                colecao.Insert(_contato);
                _contato = new Contato
                {
                    Nome = "Jefferson", Email = "jeff@net.com.br", Idade = 20
                };
                colecao.Insert(_contato);
                _contato = new Contato
                {
                    Nome = "Yuri",  Email = "yuri@net.com.br", Idade = 18
                };
                colecao.Insert(_contato);
                _contato = new Contato
                {
                    Nome = "Bianca",Email = "bibi@uol.com.br", Idade = 16
                };
                colecao.Insert(_contato);
                _contato = new Contato
                {
                    Nome = "Janice", Email = "janjan@uol.com.br", Idade = 18
                };
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public IEnumerable<Contato> GetTodosContatos()
        {
            MongoClient cliente = new MongoClient(ConnectionString);
            MongoServer server = cliente.GetServer();
            MongoDatabase database = server.GetDatabase("MongoLinq");

            var contatosLista = database.GetCollection<Contato>("Contatos");
            var query = from e in contatosLista.AsQueryable<Contato>()
                        select e;

            return query;
        }

        public IEnumerable<Contato> GetContatosPorNome(string nome)
        {
            MongoClient cliente = new MongoClient(ConnectionString);
            MongoServer server = cliente.GetServer();
            MongoDatabase database = server.GetDatabase("MongoLinq");

            var contatosLista = database.GetCollection<Contato>("Contatos");
            var query = from e in contatosLista.AsQueryable<Contato>()
                        where e.Nome == nome
                        select e;

            return query;
        }

    }
}

Na classe AcessoMongo temos os seguintes métodos:

  • ConnectionString - retorna a string de conexão a partir do arquivo App.Config;
  • CriarDados - Cria o documento MongoLing e insere alguns dados para teste;
  • CriarContato - Inclui novas informações em um documento;
  • CriarContatos - Chamada pelo método CriarDados;
  • GetTodosContatos - Retorna todos os contatos do documento;
  • GetContatosPorNome - Consulta os documentos e retorna as informações de um contato pelo seu nome.

Note que estamos referenciando o namespace :using MongoDB.Driver.Linq.

  • No formulário form1.cs inclua os seguintes controles:
  • Botões de comando: btnConsultar
  • TextBox: txtNome
  • Listbox: lstbDados
  • MenuStrip: Criar Dados e Sair

net_mglq2

Vejamos a agora o código usado no formulário para consultar informações de contatos no MongoDB:

using System;
 using System.Windows.Forms;

No menu Arquivo->Criar Dados defina o código para criar dados no MongoDB usando o método CriarDados():

net_mglq4

A chamada deste método cria o documento MongoLing e os dados conforme mostrado na figura pela ferramenta MongoVUE.

No evento Click do botão de comando Consultar inclua o código abaixo:

 private void btnConsultar_Click(object sender, EventArgs e)
        {
            AcessoMongo mongo = new AcessoMongo();

            if (string.IsNullOrWhiteSpace(txtNome.Text))
            {

                var consulta = mongo.GetTodosContatos();
                foreach (var contato in consulta)
                {
                    lstbDados.Items.Add(contato.Nome);
                }
            }
            else
            {
                var consulta = mongo.GetContatosPorNome(txtNome.Text);
                foreach (var contato in consulta)
                {
                    lstbDados.Items.Add(contato.Nome + " - " + contato.Email + " - " + contato.Idade);
                }
            }
        }

Neste código, o usuário informa o nome na caixa de texto e através do método GetContatosPorNome() será retornado os dados do contato.

net_mglq6

Se um nome não for informado será acionado o método GetTodosContatos() que retorna todos os nomes do documento MongoDB:

net_mglq5

Como vimos a execução de consultas LINQ é bastante funcional e não exige nenhum configuração da base de dados MongoDB.

Pegue o projeto exemplo aqui: MongoDB_Linq.zip

O post .NET – MongoDB – Executando consultas com LINQ apareceu primeiro em .

C# – Convertendo um DataReader para uma lista genérica e exibindo em um TreeView

$
0
0

O objetivo principal deste artigo é mostrar como podemos converter um DataReader para uma lista genérica de objetos. Eu vou usar um exemplo onde irei acessar uma tabela Alunos de um banco de dados SQL Server chamado Escola.mdf e exibir os dados em um controle TreeView.

Vou aproveitar e mostrar como preencher o TreeView usando o modo tradicional com ADO .NET e o código embutido no formulário e o modo mais indicado, onde irei criar uma classe que atuará como uma camada de acesso a dados e onde eu irei retornar uma coleção de objetos de forma que, na camada de apresentação – o formulário Windows Forms -, eu não tenha que referenciar nada relacionado a acesso aos dados, como comandos SQL ou objetos ADO .NET para acesso a dados.

Para tornar o exemplo bem simples, eu não vou criar a camada de negócios BLL e dessa forma algumas definições eu vou ter que fazer na camada de acesso a dados como a definição da string de consulta.

Para um solução mais robusta, recomenda-se a complementação do exemplo criando pelo menos a camada de negócios.

Eu vou usar o Visual Studio 2012 Express for Desktop e a linguagem C# e criar uma solução (File->New Project) Windows Forms Application, chamada PreenchendoTreeView.

No formulário form1.cs vamos os seguintes controles:

  • GroupBox
  • TreeView - treeView1
  • Button - btnCarregar - Carregar Dados (SQL)
  • Button - btnCarga - Carregar Dados (Objetos)
  • LinkLabel - lnkRecolherItens - Recolher Itens
  • Label - lblItem ; Autosize = false

O leiaute do formulário form1.cs deverá ter a seguinte aparência:

c_tv11

Abaixo vemos a estrutura da tabela Alunos do banco de dados Escola.mdf e alguns dados incluídos para o teste:

c_tv12

c_tv13

Acessando dados e preenchendo um TreeView – modo tradicional

Vamos começar usando o método tradicional, digo tradicional porque é o modo mais usado, onde todo o código é colocado no próprio formulário.

No formulário form1.cs vamos colocar todo o código necessário para acessar os dados e exibir as informações no TreeView. Para isso teremos que declarar os seguintes namespaces no início do formulário form1.cs:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Collections.Generic;

A seguir, logo após a declaração da classe form1, devemos definir as variáveis usadas para acessar os dados e a string de conexão.

SqlConnection Conn = new SqlConnection("Data Source=.\\SQLExpress; Initial Catalog=Escola; Integrated Security=True");
SqlDataReader rdr;
SqlCommand cmd;

No evento Click do botão Carregar Dados vamos incluir o código que chama a rotina que acessa os dados e preenche o TreeView:

private void btnCarregar_Click(object sender, EventArgs e)
{
   treeView1.Nodes.Clear();
   Carregar();
}

A rotina Carregar() faz todo o trabalho: acessa o banco de dados executa a consulta obtendo um DataReader e extrai os dados preenchendo o TreeView:

private void Carregar()
        {
            lblItem.Text = "";
            cmd = new SqlCommand("Select id,nome,email,curso From Alunos Order By id", Conn);

            Conn.Open();
            rdr = cmd.ExecuteReader();
            TreeNode parent = treeView1.Nodes.Add("Alunos");

            TreeNode child;
            parent.ForeColor = Color.Red;

            while (rdr.Read())
            {
                child = parent.Nodes.Add("Aluno ID: " + rdr.GetValue(0).ToString());
                child.ForeColor = Color.Blue;
                child.Nodes.Add("Nome: " + rdr.GetValue(1).ToString());
                child.Nodes.Add("Email: " + rdr.GetValue(2).ToString());
                child.Nodes.Add("Curso: " + rdr.GetValue(3).ToString());
            }
            parent.ExpandAll();

            rdr.Close();
            Conn.Close();
        }

A execução do código gera o seguinte resultado:

c_tv14Apenas para conhecimento, a seguir temos o código que obtém um item selecionado do TreeView e o exibe em um controle Label no formulário:

private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
   {
            int IDSelecionado = treeView1.SelectedNode.Index;
            String NomeSelecinado = treeView1.SelectedNode.Text;
            lblItem.Text = NomeSelecinado.ToString();
  }

Obs: Estamos obtendo o índice do item selecionado apenas para ilustrar como fazer isso.

O código para recolher os itens do TreeView que colocamos no evento Click do controle LinkLabel esta abaixo:

 private void lnkRecolherItens_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
   {
            lblItem.Text = "";
            treeView1.CollapseAll();
   }

Para limpar os itens do TreeView usamos o treeView1.Nodes.Clear(). Já para expandir os itens do TreeView, o código é treeView1.ExpandAll().

Tudo muito simples, não é mesmo ?

Acessando dados e preenchendo um TreeView – usando as boas práticas

Você não precisa ser um Phd em padrões de projetos para usar as boas práticas no desenvolvimento de software. Existem ações que podem ser tomadas desde o início do desenvolvimento até mesmo para aplicações muito simples que garantem o mínimo de robustez e tornam o código muito mais legível e fácil de manter.

O modo tradicional usado é muito simples e funciona, mas ele nunca deve ser usado em aplicações de produção pois ele oculta em sua simplicidade grandes problemas que irão aparecer quando você precisar dar manutenção ou estender a aplicação com novas funcionalidades.

A primeira coisa que chama a atenção é que a camada de apresentação tem que conhecer como acessar os dados, e isso não é a sua função. A camada de apresentação deve apenas saber apresentar os dados ao usuário deixando a responsabilidade de saber como acessar e obter os dados para outra camada.

Chamamos isso de separação das responsabilidades em uma aplicação e aplicamos essa separação trabalhando em camadas. Isso não é um capricho ou uma moda, existe uma razão muito forte para se recomendar essa metodologia de trabalho, pois ela facilita a manutenção e permite a reutilização de código.

Vamos então criar uma camada de acesso a dados que no nosso exemplo será representada por uma classe chamada ConexaoDB. O mais correto seria criar um novo projeto do tipo Class Library e nele definir as classes, mas devido a simplicidade do nosso projeto irei criar apenas a classe que atuará como a camada de acesso a dados.

No menu PROJECT clique em Add Class e informe o nome ConexaoDB.cs e a seguir defina o código abaixo nesta classe:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Collections.Generic;

namespace PreenchendoTreeView
{
    public class ConexaoDB 
    {
        private SqlConnection _connection;

        public ConexaoDB()
        {
            try
            {
                string strConnectionString = ConfigurationManager.ConnectionStrings["ConexaoSQL"].ToString();
                _connection = new SqlConnection(strConnectionString);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public List<Aluno> GetAlunosOB()
        {
           string consulta = "Select * from Alunos";
           List<Aluno> _alunos = new List<Aluno>();
           try
            {
               _connection.Open();
               SqlCommand cmd = new SqlCommand(consulta, _connection);
               cmd.CommandType = CommandType.Text;
               SqlDataReader dr = cmd.ExecuteReader();
               while (dr.Read())
               {
                  _alunos.Add(new Aluno()
                  {
                      Id = Convert.ToInt32(dr["Id"]),
                      Nome = dr["Nome"].ToString(),
                      Email = dr["Email"].ToString(),
                      Curso = dr["Curso"].ToString()
                  });
               }
               dr.Close();             
               return _alunos;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _connection.Close();
            }
        }
    }
}

Esta classe será a responsável pelo acesso ao banco de dados e pela extração das informações da tabela. Todo o conhecimento de como acessar dados é com ela mesmo e ela deve ser uma especialista nisto e deve saber somente isso.

Um dos princípios básicos para desenho de software com o objetivo evitar as más práticas é o principio da responsabilidade única ou SRP – Single Responsability Principle. Este princípio foi introduzido por Tom DeMarco em 1979 no seu livro Structured Analysis and Systems Specification, Yourdon Press Computing Series.

O princípio da responsabilidade única é fundamental no projeto de software que reza o seguinte :

“Deve existir um e somente UM MOTIVO para que uma classe mude”

Portanto, uma classe deve ser implementada tendo apenas um único objetivo.

Quando uma classe possui mais que um motivo para ser alterada é por que provavelmente ela esta fazendo mais coisas do que devia, ou seja, ela esta tendo mais de um objetivo.

Podemos então inferir as seguintes premissas a partir da definição da responsabilidade única:

  • Baseado no princípio da coesão funcional, uma classe deve ter uma única responsabilidade;
  • Se uma classe possuir mais de uma responsabilidade, deve-se considerar sua decomposição em duas ou mais classes;
  • Cada responsabilidade é um “eixo de mudança” e as fontes de mudança devem ser isoladas.

Este conceito é fácil de entender, mas difícil de ser posto em prática.

A nossa classe ConexaoDB deve portanto ter apenas um objetivo: acessar dados. Vamos então cria a classe removendo o código responsável pelo acesso aos dados do formulário.

Nesta nossa classe temos o construtor iniciando a string de conexão que esta sendo obtida do arquivo de configuração, App.Config, usando a classe ConfigurationManager:

public ConexaoDB()
{
  try
  {
    string strConnectionString = ConfigurationManager.ConnectionStrings["ConexaoSQL"].ToString();
    _connection = new SqlConnection(strConnectionString);
  }
  catch (Exception ex)
  {
    throw ex;
  }
}

Assim temos que declarar no arquivo App.Config a string de conexão da seguinte forma:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <connectionStrings>
    <add name="ConexaoSQL" connectionString="Data Source=.\SQLExpress; Initial Catalog=Escola; Integrated Security=True;" />
  </connectionStrings>
</configuration>

O método GetAlunos() da classe ConexaoDB está definindo uma string de consulta usando o comando SQL - Select * from Alunos -, e, na verdade, isso não deveria estar definido nesse método, mas na camada de negócios. Como eu não criei esta camada, para ficar mais simples, eu embuti a definição da consulta do método, mas NUNCA faça isso em uma aplicação de produção. Primeiro por que nunca devemos usar Select *, visto que isso retorna TODOS os registros da tabela. Imagine se a tabela possuir um bilhão de registros?! Por isso sempre restringimos a quantidade de dados retornados de uma tabela usando a classe Where com um critério de filtro: Ex: Select id, nome, email from Alunos Where id < 10.

public List<Aluno> GetAlunosOB()
        {
           string consulta = "Select * from Alunos";
           List<Aluno> _alunos = new List<Aluno>();
           try
            {
               _connection.Open();
               SqlCommand cmd = new SqlCommand(consulta, _connection);
               cmd.CommandType = CommandType.Text;
               SqlDataReader dr = cmd.ExecuteReader();
               while (dr.Read())
               {
                  _alunos.Add(new Aluno()
                  {
                      Id = Convert.ToInt32(dr["Id"]),
                      Nome = dr["Nome"].ToString(),
                      Email = dr["Email"].ToString(),
                      Curso = dr["Curso"].ToString()
                  });
               }
               dr.Close();             
               return _alunos;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _connection.Close();
            }
        }

Outro detalhe importante neste método é que estamos retornando uma coleção de objetos Aluno e não um datareader. Fiz isso nesta classe por que não criei a bendita camada de negócios que seria a responsável por este tratamento. Dessa forma a nossa camada de apresentação vai receber um coleção de objetos e não um datareader o que não seria muito indicado pois ela teria que saber como tratar um DataReader e isso SÓ camada de acesso a dados deve saber.

Precisamos então definir uma classe que irá representar o nosso modelo de dados e faremos isso criando a classe Aluno (PROJECT->Add Class) e definindo o código desta classe conforme abaixo:

namespace PreenchendoTreeView
{
    public class Aluno
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Email { get; set; }
        public string Curso { get; set; }
    }
}

Esta é uma classe POCO – Plain Old CLR Object - e ela mapeia os campos definidos na tabela Alunos, onde para cada campo definimos uma propriedade com o mesmo nome.

Essa classe vai agir como um DTO – Data Transfer Object - servindo apenas para que possamos passar os dados entre as camadas. No nosso exemplo entre a classe ConexaoBD e a camada de apresentação.

Finalmente na camada de apresentação no evento Click do botão Carregar Dados temos o código que vai receber os objetos e usando um laço foreach vai percorrer e exibir as informações no TreeView:

private void btnCarga_Click(object sender, EventArgs e)
        {
            lblItem.Text = "";
            treeView1.Nodes.Clear();
            try
            {
                ConexaoDB cdb = new ConexaoDB();

                TreeNode parent = treeView1.Nodes.Add("Alunos");

                TreeNode child;
                parent.ForeColor = Color.Red;

                List<Aluno> _alunos = new List<Aluno>();
                _alunos = cdb.GetAlunosOB();

                foreach (Aluno _aluno in _alunos)
                {
                    child = parent.Nodes.Add("Aluno ID: " + _aluno.Id.ToString());
                    child.ForeColor = Color.Blue;
                    child.Nodes.Add("Nome: " + _aluno.Nome.ToString());
                    child.Nodes.Add("Email: " + _aluno.Email.ToString());
                    child.Nodes.Add("Curso: " + _aluno.Curso.ToString());
                }

                treeView1.ExpandAll();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Erro : " + ex.Message);
            }
        }

Note que tivemos que criar uma instância da classe ConexaoDB para poder usar o método GetAlunosOB() pois o método é um método de instância e não é um método estático. Poderíamos ter criado um método estático na classe ConexaoDB de forma a não precisar criar uma instância da classe para acessá-lo.

Ao criar uma instância da classe no formulário acabamos criando uma dependência, ou seja um acoplamento entre ela e o formulário, e isso não é bom mas pode se evitado usando ainjeção de dependência. Assunto que eu não vou tratar neste artigo.

A execução vai produzir o mesmo resultado:

c_tv15

Vimos neste artigos coisas importantes, partindo de uma abordagem tradicional e avançando na direção das boas práticas apenas criando uma classe e separando o código da camada de apresentação; já é um grande avanço mas o caminho é longo e para percorrê-lo precisamos de mais artigos.

Pegue o projeto completo aqui: PreenchendoTreeView.zip (sem o banco de dados)

O post C# – Convertendo um DataReader para uma lista genérica e exibindo em um TreeView apareceu primeiro em .

.NET – Design Patterns – Identificando e aplicando padrões – Parte 01

$
0
0

Antes de iniciar falando de padrões de projetos, vamos falar da base que deu origem aos mesmos. Vamos falar um pouco sobre os princípios de design ou projeto de software conhecidos também como boas práticas no desenvolvimento de software.

Quais são os princípios de projeto de software?

Princípios de projeto ou design de software representam um conjunto de diretrizes que nos ajudam a evitar que tenhamos um projeto ruim. Os princípios de design estão associados à Robert Martin, que agrupou essa informação em ”Desenvolvimento Ágil de Software: Princípios, Padrões e Práticas”. De acordo com Robert Martin, há três características importantes de um projeto ruim que devem ser evitadas:

  1. Rigidez - É difícil mudar, porque cada mudança afeta muitas outras partes do sistema;
  2. Fragilidade - Quando você faz uma alteração, partes inesperadas do sistema ‘quebram’;
  3. Imobilidade - É difícil reutilizar noutra aplicação, porque não pode ser desembaraçada da aplicação atual.

Os princípios de um projeto de software formam as bases sob as quais os padrões de projeto foram construídos. Eles são mais fundamentais que os padrões de projeto. Toda vez que você seguir os princípios de projeto, sua base de código vai se tornar mais flexível e adaptável a mudanças, bem como mais fácil de ser mantida. Vamos introduzir brevemente alguns dos princípios de concepção de software mais amplamente conhecidos e uma série de princípios conhecidos como o princípios S.O.L.I.D.

Princípios de projeto de software

Existe uma série de princípios comuns de projeto que, assim como os padrões de projeto, se tornaram as melhores práticas ao longo dos anos e ajudou a formar uma base sobre a qual a criação e manutenção de software pode ser construída. Vejamos os princípios mais importantes:

Keep It Simple, Stupid (KISS)

Keep It Simple é um princípio geral que valoriza a simplicidade do projeto e defende que toda a complexidade desnecessária seja descartada. Assim, este princípio nos guia a manter as coisas simples.

Don’t Repeat Yourself (DRY)

O princípio DRY visa evitar a repetição de qualquer parte de um sistema, abstraindo as coisas que são comuns e colocando-as em um único local. Este princípio não se preocupa apenas com código, mas com qualquer lógica que esteja duplicada em um sistema, em última análise deve existir apenas uma representação para cada pedaço de conhecimento em um sistema. Em suma, o princípio busca reduzir a duplicação de código e os problemas de manutenção resultantes.

Tell, Don’t Ask

O princípio Tell, Don’t ASK (Diga, não pergunte) está estreitamente alinhado com o encapsulamento e a atribuição de responsabilidades às suas classes. O princípio, afirma que você deve dizer aos objetos quais ações você deseja que eles realizem, em vez de realizar perguntas sobre o estado de um objeto e depois fazer uma decisão sobre a ação que você deseja realizar. Isso ajuda a alinhar as responsabilidades e evitar forte acoplamento entre as classes.

You Ain’t Gonna Need It (YAGNI )

O princípio YAGNI refere-se à necessidade de incluir apenas a funcionalidade que é necessária para a aplicação e adiar qualquer tentação de adicionar outros recursos que você pode pensar que você precisa. A metodologia de projeto que adere ao YAGNI é o desenvolvimento orientado a testes (TDD). O TDD versa sobre a escrever primeiro os testes que comprovam a funcionalidade de um sistema e, em seguida, escrever apenas o código para obter o teste.

Separation of Concerns (SoC) – Separação das responsabilidades

O princípio SoC é o processo de dissecação de um pedaço de software em características distintas que encapsulam comportamento único e dados que podem ser utilizados por outras classes. Geralmente uma responsabilidade representa uma característica ou o comportamento de uma classe. O ato de separar um programa em responsabilidades discretas aumenta significativamente a reutilização de código, manutenção e testabilidade.

Princípios de projeto S.O.L.I.D

A palavra SOLID é um acróstico onde cada letra significa a sigla de um princípio: SRP, OCP, LSP, ISP e DIP. Os princípios SOLID para programação e projeto orientados a objeto são de autoria de Robert C. Martin (mais conhecido como Uncle Bob) e datam do início de 2000.

Obs: Os acrósticos são formas textuais onde a primeira letra de cada frase ou verso formam uma palavra ou frase.

Na tabela abaixo vemos cada sigla, o seu significado e um resumo do princípio a que se refere no original e em uma tradução livre.

SRP The Single Responsibility Principle
Principio da Responsabilidade Única
Uma classe deve ter um, e somente um, motivo para mudar.
A class should have one, and only one, reason to change.
OCP The Open Closed Principle
Princípio Aberto-Fechado
Você deve ser capaz de estender um comportamento de uma classe, sem modificá-lo.
You should be able to extend a classes behavior, without modifying it.
LSP The Liskov Substitution Principle
Princípio da Substituição de Liskov
As classes derivadas devem ser substituíveis por suas classes base.
Derived classes must be substitutable for their base classes.
ISP The Interface Segregation Principle
Princípio da Segregação da Interface
Muitas interfaces específicas são melhores do que uma interface geral
Make fine grained interfaces that are client specific.
DIP The Dependency Inversion Principle
Princípio da inversão da dependência
Dependa de uma abstração e não de uma implementação.
Depend on abstractions, not on concretions.

Os princípios SOLID devem ser aplicados no desenvolvimento de software de forma que o software produzido tenha as seguintes características:

  • Seja fácil de manter, adaptar e se ajustar às constantes mudanças exigidas pelos clientes;
  • Seja fácil de entender e testar;
  • Seja construído de forma a estar preparado para ser facilmente alterado com o menor esforço possível;
  • Seja possível de ser reaproveitado;
  • Exista em produção o maior tempo possível;
  • Que atenda realmente as necessidades dos clientes para o qual foi criado.

Dependency Inversion Principle (DIP) – O princípio da injeção de dependência (DIP)

O princípio DIP versa sobre como isolar suas classes de implementações concretas e fazendo-as depender de classes abstratas ou interfaces. Ele promove o mantra de codificar para uma interface ao invés de fazê-lo para uma implementação, o que aumenta a flexibilidade dentro de um sistema garantindo que o mesmo não está fortemente acoplado a um implementação.

Dependency Injection (DI) and Inversion of Control (IoC) – Injeção de dependência e Inversão de Controle

Intimamente ligados ao principio DIP temos os princípio DI e o princípio IoC.

O principio DI é o ato de fornecer um baixo nível ou classe dependente através de um construtor, método ou propriedade. Utilizado em conjunto com DI, estas classes dependentes podem ser invertidas para interfaces ou classes abstratas que levarão a sistemas com um baixo acoplamento altamente testáveis e de fácil manutenção. Assim a DI isola a implementação de um objeto da construção do objeto do qual ele depende.

No príncipio IoC, o fluxo de um sistema de controle é invertido em relação à programação procedural. Como exemplo podemos citar um recipiente IoC, cujo objetivo é injetar serviços no código de cliente sem que o código do cliente tenha especificado a implementação concreta.

Padrões de projetos

Depois de apresentar os princípios de projeto e os princípios SOLID, vamos falar dos padrões de projeto. Eles são modelos de soluções abstratas de alto nível. Pense neles como modelos para soluções e não como soluções em si mesmo.

Infelizmente não existe nenhum framework que aplique de forma automática os padrões de projetos à sua aplicação. Ao invés disso, você normalmente faz isso refatorando o seu código e generalizando o seu problema e não abrindo um manual de receitas e copiando soluções.

Os padrões de projeto não são a panaceia universal ou a bala de prata que vai resolver tudo para você. Você tem que compreender plenamente o seu problema, generalizá-lo e, em seguida, aplicar um padrão que se ajuste ao seu cenário. No entanto, nem todos os problemas requerem um padrão de projeto. É verdade que os padrões de projeto podem ajudar a tornar simples problemas complexos, mas eles também podem tornar complexos problemas simples.

Muitos desenvolvedores após lerem um livro de padrões, caem na armadilha de tentar aplicar padrões para tudo que estão fazendo, conseguindo exatamente o oposto do que os padrões se propõem: fazer as coisas simples.

Não existe mágica: a melhor maneira de aplicar padrões é identificar o problema fundamental que você está tentando resolver e procurar uma solução que se encaixa ao seu problema, levando em conta o seu cenário que pode ser uma aplicação web, desktop, com acesso a dados, etc.

Repito mais uma vez: você não é obrigado a usar os padrões de projeto em todos os seus projetos. Se você chegou a uma solução para um problema que é simples, clara e de fácil manutenção, mas ela não se encaixa em nenhum dos 23 padrões de projeto catalogados, não se apavore; você fez a coisa certa. Se você seguir outro caminho, apenas por modismo, isso vai complicar o seu projeto.

Eu sei que apesar de todo o conhecimento que se pode ter de padrões de projeto sempre fica aquela dúvida: Como escolher e como aplicar um padrão de projeto? Bem, não existe uma receita pronta, mas podemos facilitar a nossa decisão seguindo alguns conselhos.

Você pode escolher dentre muitos padrões de projetos já existentes e catalogados. Como então identificar aquele que é mais apropriado para o seu problema?

Para saber qual padrão de projeto você pode usar e como aplicar o modelo ao seu problema específico é importante compreender e utilizar alguns conceitos:

  1. Você não pode aplicar padrões sem conhecê-los bem. O primeiro passo a dar é expandir o seu conhecimento e estudar os padrões e princípios na forma concreta e abstrata. Você pode implementar padrões de projetos de muitas formas e quanto mais você conhecer sobre as diferentes implementações possíveis mais você irá compreender sobre o real objetivo do padrão e de como um padrão pode possuir diferentes implementações.
  1. Você precisa introduzir a complexidade de um padrão de projeto no seu aplicativo? É comum para os desenvolvedores tentar usar um padrão para resolver todos os problemas quando eles estão estudando padrões. Você sempre precisa parar e pesar o tempo inicial necessário para implementar um padrão e o benefício que isso vai dar. Lembre-se do princípio KISS: Keep It Simple, Stupid ou seja mantenha as coisas simples.
  1. Generalize o seu problema. Identifique os problemas que você está enfrentando de forma mais abstrata. Veja a forma como a intenção de cada padrão e princípio está escrito, e veja se o seu problema se encaixa com o problema que um determinado padrão ou princípio está tentando resolver. Lembre-se que padrões de projetos são soluções de alto nível. Tente abstrair o seu problema e não se concentre demais nos detalhes de seu problema específico.
  1. Olhe para os padrões de natureza semelhante e para os padrões no mesmo grupo. Só porque você utilizou um padrão antes não significa que ele será sempre a escolha correta quando for resolver um problema.
  1. Encapsule o que varia. Olhe para o que provavelmente vai mudar com a sua aplicação. Se você sabe que um algoritmo vai mudar ao longo do tempo, olhe para um padrão que vai ajudar você a alterá-lo sem afetar o resto de sua aplicação.
  1. Depois de ter escolhido um padrão de projeto, esteja certo em usar a linguagem de seu padrão ao longo com a linguagem do domínio ao nomear os participantes em uma solução. Por exemplo, se você está usando o padrão Strategy para fornecer uma solução procure utilizar nomes adequados usando um vocabulário padrão em comum com a linguagem de seu domínio, isso tornará o seu código mais legível e compreensível para outros desenvolvedores com conhecimento de padrões.
  1. Quando se trata de padrões de projeto, não há substituto para estudar. Quanto mais você saber sobre cada um dos padrões, melhor equipado você estará em aplicá-los. Procure captar a intenção de cada padrão para refrescar sua memória para quando você tiver um problema e estiver procurando uma solução.
  1. Um exercício de aprendizado é tentar identificar os padrões aplicados na plataforma .NET. Por exemplo, o recurso de cache no ASP.NET usa o padrão Singleton, a criação de um novo GUID usa o padrão Factory, as classes que tratam XML usam o padrão Factory, etc. Isso ajuda no seu entendimento de como aplicar os padrões.

Estes são alguns conselhos práticos que podem ajudar você a melhor compreender como usar e aplicar os padrões de projeto, mas existe um caminho mais adequado: sujar as mãos.

Na segunda parte deste artigo eu vou mostrar um exemplo prático de como podemos identificar e aplicar padrões de projetos usando um cenário básico de um situação real.

O post .NET – Design Patterns – Identificando e aplicando padrões – Parte 01 apareceu primeiro em .

.NET – Design Patterns – Identificando e aplicando padrões – Parte 02

$
0
0

Na primeira parte deste artigo demos alguns conselhos para ajudar a identificar e aplicar padrões de projetos. Agora vamos por em prática toda essa teoria em um exemplo simples e objetivo.

É muito bom falar sobre como os padrões e princípios são importantes e podem nos ajudar, mas mais importante é vê-los em ação na prática. Com isto em mente, este artigo examina como um simples pedaço de código ASP.NET que você já deve ter visto inúmeras vezes antes pode ser melhorado com o uso de padrões de projetos.

Vamos examinar um trecho de código que você pode encontrar em um aplicativo de comércio eletrônico típico que recupera todos os produtos de uma determinada categoria.

Na figura abaixo vemos o diagrama de classe contendo uma classe ProdutoService com o método GetTodosProduto, uma classe Produto, que representa os produtos da loja, e uma classe ProdutoRepository que é usada para recuperar os produtos de um banco de dados.

net_dp21

O trabalho da classe ProdutoService é coordenar a recuperação de uma lista de produtos a partir do repositório para um dado código (ID) de categoria e depois armazenar os resultados em cache de forma que a próxima chamada possa ser executada mais rapidamente.

Vamos, então, criar um projeto usando o Visual Studio Express 2012 for web, criar as classes e examinar o código.

Abra o VS Express 2012 for web e no menu Start clique em New Project. A seguir selecione template Other Project Types-> Visual Studio Solutions -> Blank Solution , informe o nome ASPNET_PadraoProjeto e clique no botão OK.

net_dp22

A seguir, no menu FILE clique Add -> New Project e selecione o template Visual C# -> Class Library informando o nome ASPNET_PadraoProjeto_Service e clique no botão OK.

net_dp23

Será criado o projeto ASPNET_PadraoProjeto_Service e a classe Class1.cs. Renomeie o arquivo Class1.cs para Produto.cs e conforme mostrado na figura abaixo:

net_dp24

Vamos incluir uma nova classe, chamada ProdutoRepository, via menu PROJECT -> Add Class contendo o seguinte código:

net_dp25

Crie agora a classe ProdutoService via menu PROJECT -> Add Class com o código mostrado a seguir.

Observe que temos que incluir uma referência a System.Web ao projeto pois estamos usando a classe HttpContext que encapsula todas as informações do HTTP específico sobre uma solicitação HTTP individual.

net_dp26

As classes Produto e ProdutoRepository não necessitam de qualquer explicação, porque eles são simples espaços reservados neste cenário. A classe ProdutoService usando o métodoGetTodosProdutos coordena a recuperação dos produtos a partir do cache, e no caso do cache estar vazio, a recuperação dos produtos a partir do repositório e a inserção no cache.

Este é um cenário típico e encontrado com frequência.

Mas então, o que esta errado com o código acima? Você saberia identificar quais os problemas ele carrega?

  • A classe ProdutoService depende da classe ProdutoRepository. Se ProdutoRepository for alterada, vamos precisar alterar também a classe ProductService, e isso não é bom.
  • O código não pode ser testado facilmente. Sem ter uma classe ProdutoRepository que se conecte com um banco de dados real, faz com que você não possa testar o método ProdutoServicepor, causa do forte acoplamento que existe entre essas duas classes. Outro problema relacionado com o teste é a dependência com contexto HTTP para o uso com o cache dos produtos. É difícil testar o código, pois ele está muito vinculado ao contexto HTTP.
  • Você está preso ao contexto HTTP para o cache. Em seu estado atual, se você precisar usar um cache diferente para armazenamento, tal como o Velocity ou Memcached, exigiria uma alteração da classe ProdutoService e qualquer outra classe que usa o cache.

O Velocity.net é um serviço de cache distribuido que é facilmente instalado em múltiplos servidores e pode ser acessado pelo .NET
através de classes criadas no namespace System.Data.Cache.

Memcached é um sistema de cache em memória distribuido muito fácil de usar.

Agora que você já sabe o que esta de errado com o código acima vem a pergunta que não quer calar: quais as providências você deverá tomar para melhorar o código usando padrões de projetos? Vamos começar com o problema da dependência entre classe ProductService e a classe ProductRepository.

No cenário atual, a classe ProdutoService é frágil, visto que se a classe ProdutoRepository for alterada ela provavelmente terá que sofrer modificações e isso vai contra dois princípios importantes: a separação de responsabilidades e o princípio da responsabilidade única.

Como resolver esse impasse?

Aplicando o princípio da inversão de dependência

Este princípio nos diz que não podemos depender de uma classe concreta, mas de uma interface: programe para uma interface e não para uma implementação (classe concreta).

Vamos começar com o problema da dependência entre classe ProductService e a classe ProductRepository.

No cenário atual, a classe ProdutoService é frágil, visto que se a classe ProdutoRepository for alterada, ela provavelmente terá que sofrer modificações e isso vai contra dois princípios importantes: a separação de responsabilidades e o princípio da responsabilidade única.

Podemos empregar o princípio da inversão da dependência para desacoplar a classe ProdutoService da classe ProdutoRepository, fazendo com que ambas dependam de uma abstração – uma interface.

 

Vamos abrir a classe ProdutoRepository e refatorar a classe, extraindo uma interface. Você pode fazer isso usando a refatoração (o Visual Studio possui este recurso nativo) ou criando você mesmo o código.

Devemos criar a interface IProdutoRepository, via menu PROJECT-> Add New Item, com o código mostrado a seguir:

net_dp28

A seguir vamos ajustar a classe ProdutoRepository para implementar a nova interface recém-criada:

net_dp29

Finalmente precisamos atualizar a classe ProdutoService para garantir que ele faz referência a interface ao invés da classe concreta:

net_dp2a

O que você conseguiu através da introdução de uma nova interface?

A classe ProductService agora depende apenas de uma abstração em vez de uma classe concreta, o que significa que a classe agora é completamente ignorante de qualquer implementação, assegurando que ela é menos frágil e que o seu código base é menos sujeito a mudanças.

No entanto, existe um pequeno problema: a classe ProdutoService ainda é responsável pela implementação concreta, e, atualmente é impossível testar o código sem uma classeProdutoRepository válida.

O que fazer para resolver isso?

Aqui entra a Injeção de dependência para nos ajudar a solucionar este problema.

Aplicando o princípio da Injeção de dependência

A classe ProdutoService ainda está acoplada à implementação concreta da classe ProdutoRepository porque é de responsabilidade da classe ProdutoService criar essa instância. Isto pode ser visto no construtor da classe.

Usando a injeção de dependência podemos mover a responsabilidade de criar a implementação de ProdutoRepository para fora da classe ProdutoService e fazer a injeção da dependência através do construtor da classe, tal como pode ser visto na listagem de código a seguir:

net_dp2b

Note que removemos a linha de código que implementa a classe concreta:

_produtoRepository = new ProdutoRepository();

Injetando a dependência da interface:

_produtoRepository = produtoRepository;

Isso permite que um substituto possa ser passado para a classe ProdutoService durante os testes, o que permite que você teste a classe isoladamente.

Ao remover a responsabilidade de obter dependências de ProdutoService, você está garantindo que a classe adere ao princípio da responsabilidade única, visto que agora ela só se preocupa com a recuperação dos dados do cache ou repositório e não mais em criar uma implementação concreta de IProductRepository.

A injeção de dependência pode ser aplicada de três maneiras distintas: via Construtor, Método e Propriedade. Usamos neste nosso exemplo a injeção de dependência via construtor.

A última coisa que precisamos fazer agora é resolver a dependência do contexto HTTP para o cache. Para isso, vamos contratar os serviços de um padrão de design simples.

Refatorando o padrão Adapter

Como não temos o código-fonte para a classe de contexto HTTP, não podemos simplesmente criar uma interface da mesma forma que fizemos para a classe ProdutoRepository.

Felizmente, esse tipo de problema foi resolvido inúmeras vezes antes, e existe um padrão de projeto pronto para nos ajudar: o padrão de projeto Adapter.

O padrão Adapter basicamente traduz uma interface para uma classe em uma interface compatível para que você possa aplicar esse padrão e mudar a API Context cache HTTP para uma API compatível que você deseje usar. Então, você pode injetá-la através de uma interface para a classe ProdutoService usando o príncipio da injeção de dependência.

Vamos criar uma interface chamada ICachePersistencia, via menu PROJECT-> Add New Item -> Interface, com o seguinte código:

net_dp2c

Agora que temos a nova interface, podemos atualizar a classe ProdutoService para usá-la ao invés de usar a implementação do contexto HTTP.

Temos a seguir o código da classe ProdutoService alterado, já usando a interface ICachePersistencia:

net_dp2d

Agora o nosso problema é que a API Context Cache HTTP não pode implementar implicitamente a nova interface ICachePersistencia.

Como o padrão Adapter pode ser usado para nos salvar desse impasse? Qual o objetivo do padrão Adapter? É converter a interface de uma classe em outra interface esperada pelos clientes. E é exatamente isso que precisamos aqui.

Abaixo vemos o diagrama UML representando o padrão Adapter:

fonte: https://www.l2f.inesc-id.pt/~david/wiki/pt/index.php/Adapter_Pattern_(padr%C3%A3o_de_desenho)

fonte: https://www.l2f.inesc-id.pt/~david/wiki/pt/index.php/Adapter_Pattern_(padr%C3%A3o_de_desenho)

Como vemos na figura, um cliente tem uma referência a uma abstração - o Target. Neste caso, esta é a interface ICachePersistencia.

O adaptador é uma implementação da interface de Target e simplesmente delega o método de operação para o Adaptee que gera o seu próprio método SpecificOperation. Vemos que o adaptador simplesmente envolve uma instância do Adaptee e delega o trabalho dele enquanto implementa o contrato da interface Target.

Ajustanto o diagrama para o nosso exemplo chegamos ao seguinte diagrama:

net_dp2f

Vemos a classe do projeto e a classe Adapter que precisamos implementar usando o padrão Adapter com o contexto HTTP cache de API.

A classe HttpContextCacheAdapter é um invólucro para o cache Context HTTP e delega o trabalho para os seus métodos. Para implementar o padrão Adapter precisamos criar classe HttpContextCacheAdapter.

Assim, inclua uma nova classe no projeto, via menu PROJECT -> Add Class, chamada HttpContextCacheAdapter.cs com o seguinte código no projeto:

net_dp2g

Agora é mais fácil implementar uma solução de cache nova, sem afetar qualquer código existente. Por exemplo, se você quiser usar o Velocity, tudo que você precisa fazer é criar um adaptador que permite que a classe ProdutoService possa interagir com o provedor de cache de armazenamento através da interface comum ICachePersistencia.

O padrão Adapter também é simples. Seu único propósito é fazer com que as classes com interfaces incompatíveis trabalhem em conjunto.

O adaptador não é o único padrão que pode nos ajudar com o cache de dados. Existe um padrão de projeto chamado Proxy que também pode ser usado mas isso é assunto para outro artigo.

Vimos assim como a aplicação de alguns padrões básicos pode desacoplar o seu código, tornando-o mais robusto e testável.

Pegue o projeto completo aqui: ASPNET_PadraoProjeto.zip

O post .NET – Design Patterns – Identificando e aplicando padrões – Parte 02 apareceu primeiro em .

ASP .NET MVC – Realizando a paginação de dados

$
0
0

Neste artigo veremos como realizar a paginação de dados em aplicações ASP .NET MVC.

Existem muitas maneiras de realizarmos a paginação de dados em aplicações web e neste artigo eu vou mostrar como fazer isso usando o recurso PagedList.

Eu vou começar mostrando o uso do PagedList em uma aplicação ASP .NET MVC 3 da forma mais simples possível.

Eu vou acessar a tabela Alunos do banco de dados Cadastro.mdf criado no SQL Server 2012 (LocalDB). Esta tabela possui a estrutura e os dados conforme abaixo:

01

02

Abra o Visual Studio 2012 Express for web e clique em New Project.

A seguir selecione o template Visual C# ou Visual Basic -> Web e ASP .NET MVC 3 Web Application informando um nome (Mvc_Paginacao) e clique no botão OK.

03

A seguir selecione o template Basic e o engine Razor e clique em OK.

04
Vamos definir o Model usando o Entity Framework para acessar os dados da tabela Alunos.

Clique com o botão direito do mouse sobre a pasta Models e selecione Add -> New Item.

Clique na guia Data e selecione ADO .NET Entity Framework e informe o nome Cadastro.edmx.

Selecione Generate from DataBase e clique em Next>.

Selecione a conexão com o banco de dados Cadastro.mdf e clique em Next>.

05

Selecine a tabela Alunos e marque as opções Pluralize or Singularize generated object names e clique em Finish.

Agora na pasta Controllers abra o arquivo HomeController e altere o código do método Index conforme abaixo:

      public ActionResult Index()
        {
            var contexto = new CadastroEntities();
            var listaAlunos = contexto.Alunos.ToList();
            return View(listaAlunos);
        }

Agora vamos ajustar a view Index na pasta Views/Home alterando o seu código para exibir os dados da tabela Alunos conforme a seguir:

@model IEnumerable<Mvc_Paginacao.Models.Aluno>

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>
<table>
    <tr>
        <th>Nome</th>
        <th>Email</th>
        <th>Curso</th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>@Html.DisplayFor(modelItem => item.Nome)</td>
            <td>@Html.DisplayFor(modelItem => item.Email)</td>
            <td> @Html.DisplayFor(modelItem => item.Curso)</td>
        </tr>
    }
</table>

Se você executar a aplicação neste momento irá obter o seguinte resultado:

06

Mas cadê a paginação??

Calma!! Vamos implementar isso agora…

Vamos baixar e instalar o componente PagedList em nosso projeto.

No menu TOOLS clique em Library Package Manager -> Manage Nuget Packets for Solution

Clique na guia OnLine e digite PagedList na caixa Search Online.

Localize e selecione o pacote PagedList.Mvc e clique no botão Install e a seguir confirme a instalação.

07

Após instalar o recurso vamos alterar o método Index nosso controlador HomeControler na pasta Controllers modificando o seu código conforme abaixo:

using System.Linq;
using System.Web.Mvc;
using Mvc_Paginacao.Models;
using PagedList;

namespace Mvc_Paginacao.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index(int? pagina)
        {
            var contexto = new CadastroEntities();
            var listaAlunos = contexto.Alunos.ToList();
            int paginaTamanho = 4;
            int paginaNumero = (pagina ?? 1);

            return View(listaAlunos.ToPagedList(paginaNumero, paginaTamanho));
        }

        public ActionResult About()
        {
            return View();
        }
    }
}

Note que definimos o namespace PagedList.

Agora temos que ajustar a view Index na pasta Views/Home para poder realizar a paginação.

@model PagedList.IPagedList<Mvc_Paginacao.Models.Aluno>

@{
    ViewBag.Title = "Home Page";
}

<h2>@ViewBag.Message</h2>
<table>
    <tr>
        <th>Nome</th>
        <th>Email</th>
        <th>Curso</th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>@Html.DisplayFor(modelItem => item.Nome)</td>
            <td>@Html.DisplayFor(modelItem => item.Email)</td>
            <td> @Html.DisplayFor(modelItem => item.Curso)</td>
        </tr>
    }
</table>   
<div>
    Página @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
    of @Model.PageCount
     
    @if (Model.HasPreviousPage)
    {
        @Html.ActionLink("<<", "Index", new { pagina = 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
        @Html.Raw(" ");
        @Html.ActionLink("< Anterior", "Index", new { pagina = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
    }
    else
    {
        @:<<
        @Html.Raw(" ");
        @:< Anterior
    }
     
    @if (Model.HasNextPage)
    {
        @Html.ActionLink("Próxima >", "Index", new { pagina = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
        @Html.Raw(" ");
        @Html.ActionLink(">>", "Index", new { pagina = Model.PageCount, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
    }
    else
    {
        @:Próxima >
        @Html.Raw(" ")
        @:>>
    }
</div>

O código mostra as alterações que fizemos.

Alteramos a referência para o model: @model PagedList.IPagedList<Mvc_Paginacao.Models.Aluno> e incluímos o código para paginar o resultado. Veja o resultado abaixo:

08

Agora sim, temos a paginação dos dados.

Em outro artigo vou mostrar como fazer a paginação e a ordenação em uma aplicação ASP .NET MVC 4.

Pegue o projeto completo aqui: Mvc_Paginacao.zip (sem as referências)

O post ASP .NET MVC – Realizando a paginação de dados apareceu primeiro em .

Viewing all 293 articles
Browse latest View live