C# - ListView - CRUD básico com SQL Server em 3 camadas - I


 Neste artigo vamos recordar como realizar o CRUD básico usando o banco de dados SQL Server em uma aplicação Windows Forms aplicando uma arquitetura em camadas.

Vamos realizar a manutenção de usuários que estão armazenados em uma tabela Usuarios no banco de dados chamado Cadastro.mdf no SQL Server.

Apresentando os modelo de dados

A seguir temos a figura exibindo a estrutura da tabela Usuarios e alguns dados que eu vou usar para testar o projeto.

O Script usado para gerar a tabela :

USE [Cadastro]
GO
CREATE TABLE [dbo].[Usuarios](
	[UsuarioId] [int] IDENTITY(1,1) NOT NULL,
	[Nome] [nvarchar](150) NOT NULL,
	[Senha] [nvarchar](150) NOT NULL,
	[Email] [nvarchar](150) NOT NULL,
 CONSTRAINT [PK_Usuarios] PRIMARY KEY CLUSTERED 
(
	[UsuarioId] ASC
);

Neste exemplo vamos usar os recursos da ADO .NET e trabalhar com uma camada de acesso a dados e uma camada de negócios separando assim as funcionalidades da nossa aplicação. Mesmo em projetos pequenos esta é uma boa prática a qual você deve se habituar.

Apresentando a aplicação

Antes de iniciar o projeto vamos dar uma olhada no formulário principal e explicar algumas funcionalidades do mesmo.

Abaixo temos o formulário principal do projeto onde é realizada a manutenção dos usuários da tabela Usuarios:

O Controle ListView esta sendo usado para exibir e manipular os dados de forma que ao clicar em uma linha do controle os dados são exibidos nas caixas de texto do formulário. Além disso vamos aplicar o efeito zebrado no controle e formatar as colunas.

Estamos usando também uma camada de acesso a dados simples com os recursos da ADO .NET (sem LINQ nem Entity Framework) e uma camada de negócios de forma a ter uma arquitetura em 3 camadas onde:

Criando o projeto Windows Forms e os projetos Class Library

Vamos então criar um projeto do tipo Windows Forms App(.NET Framework) no VS 2017 Community no menu File -> New Project, com o nome WF_Crud;

A seguir no menu File-> Add -> New Project, vamos incluir dois projetos do tipo Windows Desktop -> Class Library(.NET Framework) com o nome BLL e DAL.

Nossa solução deverá conter 3 projetos conforme mostra a estrutura na janela Solution Explorer:

  1. WF_Crud - Camada de interface (UI)
  2. BLL - Camada de negócios
  3. DAL - Camada de acesso aos dados

Referenciando os projetos

Neste projeto a nossa camada de interface (UI) vai acessar a camada de negócios e por sua vez vai acessar a camada de acesso a dados.

Como temos 3 projeto distintos precisamos referenciar as dependências entre os projetos para que cada projeto tenha acesso ao código do projeto do qual depende.

Clique com o botão direito sobre o projeto WF_Crud e a seguir em Add -> Reference;

Como o projeto WF_Crud precisa acessar o projeto BLL na janela Reference, selecione Projects e marque a opção BLL clicando a seguir no botão OK:

Repita a operação para o projeto BLL e marque o projeto DAL:

Pronto. Agora podemos implementar o código em cada projeto.

Criando a camada de acesso aos dados - DAL

No projeto DAL vamos incluir uma classe chamada AcessoHelper que será a nossa camada de acesso aos dados.

Esta classe vai implementar a interface IDisposable e vai usar os seguintes namespaces:

using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;

A seguir vamos definir a classe AcessoHelper implementando a interface IDisposable. Geralmente implementamos esta interface quando vamos tratar com tipos não gerenciados cono conexões de banco de dados e mesmo com código gerenciado como as classe .NET como System.IO.FileStream e assim termos mais segurança de que os objetos serão liberados após sua utilização pelo coletor de lixo.

Dessa forma vamos declarar a nossa classe da seguinte forma:

public class AcessoHelper : IDisposable
{

    private DbConnection objConnection;
    private DbCommand objCommand;
    private DbProviderFactory objFactory = null;

    public enum ConnectionState
    {
      MantemConexaoAberta,
      FechaConexaoAoSair
    }

    public AcessoHelper()
    {
      objFactory = SqlClientFactory.Instance;
      objConnection = objFactory.CreateConnection();
      objCommand = objFactory.CreateCommand();
      objConnection.ConnectionString = GetStringConnection();
      objCommand.Connection = objConnection;
    }

    public void Dispose()
    {
       objConnection.Close();
       objConnection.Dispose();
       objCommand.Dispose();
   }

}

Neste código temos:

O construtor inicia os objetos e define a string de conexão e a conexão com o banco de dados. O próximo passo é definir o método GetStringConnection() que obtém a string de conexão:

    public string GetStringConnection()
    {
        string strConexao = @"Data Source = Macoratti; Initial Catalog = Cadastro; Integrated Security = True";
        // Retorna a string de conexão
        return strConexao;
    }

A próxima etapa será definir os métodos para acessar e persistir informações no banco de dados. Para tornar as coisas mais simples eu só vou definir os métodos que o projeto irá usar e o nosso projeto irá realizar as seguintes operações:

A seguir temos os métodos ExecuteNonQuery definidos na classe AcessoHelper da camada de acesso a dados:

    public int ExecuteNonQuery(string query)
    {
        return ExecuteNonQuery(query, CommandType.Text, ConnectionState.FechaConexaoAoSair);
    }

    public int ExecuteNonQuery(string query, CommandType commandtype)
    {
        return ExecuteNonQuery(query, commandtype, ConnectionState.FechaConexaoAoSair);
    }

    public int ExecuteNonQuery(string query, ConnectionState connectionstate)
    {
        return ExecuteNonQuery(query, CommandType.Text, connectionstate);
    }

    public int ExecuteNonQuery(string query, CommandType commandtype, ConnectionState connectionstate)
    {
        objCommand.CommandText = query;
        objCommand.CommandType = commandtype;
        int i = -1;
        try
        {
            if (objConnection.State == System.Data.ConnectionState.Closed)
                objConnection.Open();
            i = objCommand.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            throw (ex);
        }
        finally
        {
            objCommand.Parameters.Clear();
            if (connectionstate == ConnectionState.FechaConexaoAoSair)
                objConnection.Close();
        }
        return i;
    }

Porque definimos 4 métodos ExecuteNonQuery ?

Definimos 4 métodos sobrecarregados para ter mais flexibilidade de forma a podemos passar para o métodos parâmetros diferentes conforme a nossa necessidade. Assim temos:

Nosso projeto iremos usar somente a primeira opção mas eu resolvi deixar os demais métodos para você perceber uma característica de uma linguagem orientada a objetos : a sobrecarga de métodos.

A seguir temos a definição dos métodos ExecuteReader da camada de acesso a dados:

  public DbDataReader ExecuteReader(string query)
    {
        return ExecuteReader(query, CommandType.Text, ConnectionState.FechaConexaoAoSair);
    }
    public DbDataReader ExecuteReader(string query, CommandType commandtype)
    {
        return ExecuteReader(query, commandtype, ConnectionState.FechaConexaoAoSair);
    }
    public DbDataReader ExecuteReader(string query, ConnectionState connectionstate)
    {
        return ExecuteReader(query, CommandType.Text, connectionstate);
    }
    public DbDataReader ExecuteReader(string query, CommandType commandtype, 
ConnectionState connectionstate)
    {
        objCommand.CommandText = query;
        objCommand.CommandType = commandtype;
        DbDataReader reader = null;
        try
        {
            if (objConnection.State == System.Data.ConnectionState.Closed)
                objConnection.Open();
            if (connectionstate == ConnectionState.FechaConexaoAoSair)
                reader = objCommand.ExecuteReader(CommandBehavior.CloseConnection);
            else
                reader = objCommand.ExecuteReader();
        }
        catch (Exception ex)
        {
            throw (ex);
        }
        finally
        {
            objCommand.Parameters.Clear();
        }
        return reader;
    }

Aqui também definimos 4 métodos sobrecarregados mas na verdade o projeto irá usar somente o primeiro onde passamos somente a string de consulta para o método.

Além destes poderíamos definir também os métodos :

Nota:  Vou deixar a implementação destes métodos em um arquivo texto na pasta DAL.

Como estamos vamos usar parâmetros nas instruções SQL e consultas precisamos definir um método para podermos passar os parâmetros que serão usados nas instruções SQL. Para isso vamos criar o método AddParameter() conforme mostrado a seguir:

    public int AddParameter(string name, object value)
    {
        DbParameter p = objFactory.CreateParameter();
        p.ParameterName = name;
        p.Value = value;
        return objCommand.Parameters.Add(p);
    }
    public int AddParameter(DbParameter parameter)
    {
        return objCommand.Parameters.Add(parameter);
    }

Para encerrar vamos definir um método para gerar um Hash da senha de forma que vamos armazenar não o texto da senha mas o seu HASH aumentando assim a segurança da informação. Segue abaixo o código do método GerarHash que obtém um texto e retorna um hash do mesmo :

    public string GerarHash(string Valor)
    {
        System.Security.Cryptography.SHA1Managed Sha = new System.Security.Cryptography.SHA1Managed();
        Sha.ComputeHash(System.Text.Encoding.Default.GetBytes(Valor));
        return Convert.ToBase64String(Sha.Hash);
    }

Nota Este método não deveria estar nesta classe pois ele trata da geração de Hash e não tem nada a ver com acesso aos dados. Aqui estamos violando o padrão SRP. Mas para ficar mais simples vou implementar o método aqui mesmo.

Com esses métodos já temos a camada de acesso a dados pronta para nos dar o suporte necessário para consultar e persistir informações.

A próxima etapa é definir uma camada de negócios para receber as instruções da camada de interface e por último definir a camada de interface e criar as requisições necessárias as demais camadas para acessar e persistir informações dos usuários.

Aguarde a próxima parte do artigo.

(disse Jesus) - "Porque por tuas palavras serás justificado, e por tuas palavras serás condenado."
Mateus 12:37

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ?

Referências:


José Carlos Macoratti