Na primeira
parte deste artigo eu apresentei o
modelo de desenvolvimento em duas camadas muito usado em aplicações
cliente-servidor. Vou continuar a criar os demais métodos da camada de acesso a
dados categoriaDAL.
Irei criar os métodos básicos que realizam as
operações CRUD(Create, Read, Update , Delete)
conforme definidos a seguir:
- List<CategoriaDetails>
GetCategorias() – Obtém uma relação de
categorias; - DeleteCategoria(int
categoriaID) – Exclui uma categoria com base
no código da categoria; - UpdateCategoria(CategoriaDetails
categoria) – Atualiza as categorias; - InsertCategoria(CategoriaDetails
categoria) – Inclui uma nova categoria; -
CategoriaDetails
GetCategoriaByID(int
categoryID – Retorna uma categoria pelo seu código;
Você não notou nada de diferente nas definições do
métodos acima?
Lembra do primeiro método que criamos no artigo
anterior, DataTable GetCategorias()?
Observe que ele retorna um objeto DataTable. Nada
de mais, não é mesmo?
Agora observe os demais métodos que definimos
acima. Eles não retornam DataSets ou DataTables mas
retornam objetos relacionados com o nosso domínio: categoria e coleção de
categorias.
Sim, resolvi mudar o foco do artigo e, como iria
apenas criar os demais métodos CRUD, resolvi mostrar um maneira mais
eficiente e próxima da orientação a objetos na maneira como vamos tratar a nossa
camada de acesso a dados.
Na verdade vemos muitos artigos publicados na web
que usam a linguagem VB .NET ou C# e mostram como realizar as mais variadas
tarefas usando a forma procedural de escrever código. No caso da criação da
camada de acesso a dados, isso é mais relevante pois temos à disposição recursos
da linguagem orientada a objetos mas não vemos isso sendo aplicado.
Então, a partir desse ponto, eu vou definir um
desenho para nossa camada de acesso a dados que vai usar os recursos da
orientação a objetos com o objetivo de facilitar a reutilização de código e a
manutenção.
Desenhando uma
arquitetura em camadas com recursos da POO
Ao invés de escrever uma classe DAL diretamente,
vamos primeiro definir uma classe abstrata que irá definir uma interface publica
da classe contendo os métodos para acesso e manutenção de dados. Essa será nossa
classe base.
O código de acesso estará em uma classe concreta
que irá herdar da nossa classe base e irá fornecer a implementação para os
métodos abstratos.
Vamos definir também uma classe abstrata que irá
conter os métodos de acesso a dados e uma classe que irá representa os objetos
do nosso domínio no caso categorias.
Abaixo temos uma figura que representa a hierarquia
da classes do nosso modelo:

A seguir iremos
cria um diagrama UML para representar as classes. Antes vamos recordar
alguns conceitos:
Na UML, atributos são mostrados com pelo menos seu
nome, e podem também mostrar seu tipo, valor inicial e outras propriedades.
Atributos podem também ser exibidos com sua visibilidade:
- + indica atributos públicos
- # indica atributos protegidos
- – indica atributos privados
Operações (métodos) também são exibidos com pelo menos seu nome, e podem
também mostrar seus parâmetros e valores de retorno. Operações podem, como
os Atributos, mostrar sua visibilidade:
- + indica operações públicas
- # indica operações protegidas
- – indica operações privadas
Obs:
A representação de uma classe
abstrata em UML é quase igual à representação de uma classe
concreta, a única diferença é o estilo da fonte do nome da classe,
que, neste caso, está em itálico.
A seguir vemos o diagrama de classe exibindo os
atributos e métodos e a associação entre as classes. A associação é do tipo
Generalização que representa o conceito de herança.

- – Nossa classe abstrata que contém
os métodos para acesso a dados será chamada de: DataAccess.cs -
– A classe abstrata que contém os métodos para
acesso e manutenção será chamada de: CategoriaProvider.cs -
– A classe concreta que irá implementar os
métodos abstratos será chamada de: CategoriaDAL.cs -
– A classe que irá representar nosso objeto de
domínio será chamada de: CategoriaDetails.cs
Todos estes arquivos foram criados na
pasta App_Code através do menu principal na opção WebSite -> Add New Item, selecionando o templateClass e informando
o nome correspondente e a linguagem C#.
Vemos abaixo na janela Solution Explorer
a estrutura da solução e os arquivos criados na pasta App_Code.

Vejamos a seguir o código de cada uma
delas:
1. Código da classe abstrata
DataAccess.cs que irá conter somente os métodos de acesso a dados.
Essa classe é especializada em acesso a dados.
É uma classe abstrata e por isso não
pode ser instanciada diretamente e seus métodos são todos protected, o que
indica que somente podem ser acessados pela classe e por classe que dela
herdarem.
using System.Collections.Generic;<br /><br />using System.Data.Common;<br /><br />/// <summary><br /><br />/// descricao para DataAccess<br /><br />/// </summary><br /><br />public abstract class DataAccess<br /><br />{<br /><br /> public DataAccess()<br /><br /> {<br /><br /> }<br /><br /><br /><br /> private string _connectionString = "";<br /><br /> protected string ConnectionString<br /><br /> {<br /><br /> get { return _connectionString; }<br /><br /> set { _connectionString = value; }<br /><br /> }<br /><br /> protected int ExecuteNonQuery(DbCommand cmd)<br /><br /> {<br /><br /> foreach (DbParameter param in cmd.Parameters)<br /><br /> {<br /><br /> if (param.Direction == ParameterDirection.Output ||<br /><br /> param.Direction == ParameterDirection.ReturnValue)<br /><br /> {<br /><br /> switch (param.DbType)<br /><br /> {<br /><br /> case DbType.AnsiString:<br /><br /> case DbType.AnsiStringFixedLength:<br /><br /> case DbType.String:<br /><br /> case DbType.StringFixedLength:<br /><br /> case DbType.Xml:<br /><br /> param.Value = "";<br /><br /> break;<br /><br /> case DbType.Boolean:<br /><br /> param.Value = false;<br /><br /> break;<br /><br /> case DbType.Byte:<br /><br /> param.Value = byte.MinValue;<br /><br /> break;<br /><br /> case DbType.Date:<br /><br /> case DbType.DateTime:<br /><br /> param.Value = DateTime.MinValue;<br /><br /> break;<br /><br /> case DbType.Currency:<br /><br /> case DbType.Decimal:<br /><br /> param.Value = decimal.MinValue;<br /><br /> break;<br /><br /> case DbType.Guid:<br /><br /> param.Value = Guid.Empty;<br /><br /> break;<br /><br /> case DbType.Double:<br /><br /> case DbType.Int16:<br /><br /> case DbType.Int32:<br /><br /> case DbType.Int64:<br /><br /> param.Value = 0;<br /><br /> break;<br /><br /> default:<br /><br /> param.Value = null;<br /><br /> break;<br /><br /> }<br /><br /> }<br /><br /> }<br /><br /> return cmd.ExecuteNonQuery();<br /><br /> }<br /><br /> protected IDataReader ExecuteReader(DbCommand cmd)<br /><br /> {<br /><br /> return ExecuteReader(cmd, CommandBehavior.Default);<br /><br /> }<br /><br /> protected IDataReader ExecuteReader(DbCommand cmd, CommandBehavior behavior)<br /><br /> {<br /><br /> return cmd.ExecuteReader(behavior);<br /><br /> }<br /><br /> protected object ExecuteScalar(DbCommand cmd)<br /><br /> {<br /><br /> return cmd.ExecuteScalar();<br /><br /> }<br /><br />}2. A classe CategoriaProvider.cs
possui a assinatura dos seguintes métodos CRUD para acesso e
manutenção dos dados:
public abstract List<CategoriaDetails> GetCategorias();<br /> public abstract CategoriaDetails GetCategoriaByID(int categoriaID);<br /> public abstract bool DeletaCategoria(int categoriaID);<br /> public abstract bool UpdateCategoria(CategoriaDetails categoria);<br /> public abstract int InsertCategoria(CategoriaDetails categoria);Também possui um método que retorna
detalhes sobre uma categoria:
GetCategoriaFromReader e outro que retorna
uma lista de categorias:
GetCategoriaCollectionFromReader.
É uma classe abstrata e herda da
classe DataAcess tendo, desta forma, acesso a todos os métodos desta
classe.
using System.Xml.Linq;<br /><br />using System.Collections.Generic;<br /><br /> /// <summary><br /><br /> /// categoriaProvider<br /><br /> /// </summary><br /><br /> public abstract class CategoriaProvider : DataAccess<br /><br /> {<br /><br /> public CategoriaProvider()<br /><br /> {<br /><br /> this.ConnectionString = ConfigurationManager.ConnectionStrings["MacorattiConnectionString"].ConnectionString);<br /><br /> }<br /><br /> public abstract List<CategoriaDetails> GetCategorias();<br /><br /> public abstract CategoriaDetails GetCategoriaByID(int categoriaID);<br /><br /> public abstract bool DeletaCategoria(int categoriaID);<br /><br /> public abstract bool UpdateCategoria(CategoriaDetails categoria);<br /><br /> public abstract int InsertCategoria(CategoriaDetails categoria);<br /><br /> /// <summary><br /><br /> /// Retorna uma nova instancia de CategoriaDetails<br /><br /> /// preenchida com os dados atuais do DataReader<br /><br /> /// </summary><br /><br /> protected virtual CategoriaDetails GetCategoriaFromReader(IDataReader reader)<br /><br /> {<br /><br /> return new CategoriaDetails(<br /><br /> (int)reader["categoriaid"],<br /><br /> reader["nome"].ToString(),<br /><br /> reader["descricao"].ToString(),<br /><br /> reader["imagemurl"].ToString());<br /><br /> }<br /><br /> /// <summary><br /><br /> /// Retorna uma coleção de objetos CategoriaDetails<br /><br /> /// com os dados lidos a partir do do DataReader<br /><br /> /// </summary><br /><br /> protected virtual List<CategoriaDetails> GetCategoriaCollectionFromReader(IDataReader reader)<br /><br /> {<br /><br /> List<CategoriaDetails> categorias = new List<CategoriaDetails>();<br /><br /> while (reader.Read())<br /><br /> categorias.Add(GetCategoriaFromReader(reader));<br /><br /> return categorias;<br /><br /> }<br /><br />}3. O código da classe
CategoriaDetails.cs abaixo representa o objeto Categorias e possui
dois construtores e 4 propriedades que permitem o acesso aos dados do objeto.
// <summary><br /><br />/// Descrição de CategoriaDetails<br /><br />/// </summary><br /><br />public class CategoriaDetails<br /><br />{<br /><br /> public CategoriaDetails()<br /><br /> {}<br /><br /> public CategoriaDetails(int id, string categoriaNome, string descricao, string imagemUrl)<br /><br /> {<br /><br /> this.categoriaid = id;<br /><br /> this.nome = categoriaNome;<br /><br /> this.descricao = descricao;<br /><br /> this.imagemurl = imagemUrl;<br /><br /> }<br /><br /> private int _id = 0;<br /><br /> public int categoriaid<br /><br /> {<br /><br /> get { return _id;}<br /><br /> set { _id = value;}<br /><br /> }<br /><br /> private string _categoriaNome = "";<br /><br /> public string nome<br /><br /> {<br /><br /> get { return _categoriaNome; }<br /><br /> set { _categoriaNome = value; }<br /><br /> }<br /><br /> private string _descricao = "";<br /><br /> public string descricao<br /><br /> {<br /><br /> get { return _descricao; }<br /><br /> set { _descricao = value; }<br /><br /> }<br /><br /> private string _imagemUrl = "";<br /><br /> public string imagemurl<br /><br /> {<br /><br /> get { return _imageUrl; }<br /><br /> set { _imageUrl = value; }<br /><br /> }<br /><br />}4. A classe CategoriaDAL é nossa classe
concreta que implementa os métodos e herda da classe CategoriaProvider.
Por isso tem que implementar os métodos
public abstract List<CategoriaDetails> GetCategorias();<br /> public abstract CategoriaDetails GetCategoriaByID(int categoriaID);<br /> public abstract bool DeletaCategoria(int categoriaID);<br /> public abstract bool UpdateCategoria(CategoriaDetails categoria);<br /> public abstract int InsertCategoria(CategoriaDetails categoria);Definidos na classe CategoriaProvider.
using System.Xml.Linq;<br /><br />using System.Data.SqlClient;<br /><br />/// <summary><br /><br />/// classe para acesso a dados para a tabela Categorias<br /><br />/// </summary><br /><br />public class categoriaDAL : CategoriaProvider<br /><br />{<br /><br /> public categoriaDAL()<br /><br /> {}<br /><br /> //este método foi criado na primeira parte do artigo e não será usada<br /><br /> //pois foi substituida pelo método de mesma assinatura que retorna uma lista de categorias<br /><br /> // o nome do método foi alterado para GetCategoriasDataTable()<br /> public static DataTable GetCategoriasDataTable()<br /><br /> {<br /><br /> using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MacorattiConnectionString"].ConnectionString))<br /><br /> {<br /><br /> try<br /><br /> {<br /><br /> conn.Open();<br /><br /> SqlCommand cmd = new SqlCommand("SELECT * FROM Categorias", conn);<br /><br /> DataTable tbl = new DataTable();<br /><br /> tbl.Load(cmd.ExecuteReader(CommandBehavior.CloseConnection));<br /><br /> return tbl;<br /><br /> }<br /><br /> catch (SqlException ex)<br /><br /> {<br /><br /> throw new Exception ("Erro ao acessar Categorias" + ex.Message);<br /><br /> }<br /><br /> }<br /><br /> }<br /><br /> ///////////////////////////////////////////////////////////////////////////////////////////////<br /> /// <summary><br /><br /> /// Retorna uma coleção com todas as categorias<br /><br /> /// </summary><br /><br /> public override List<CategoriaDetails> GetCategorias()<br /><br /> {<br /><br /> using (SqlConnection cn = new SqlConnection(this.ConnectionString))<br /><br /> {<br /><br /> SqlCommand cmd = new SqlCommand("SELECT * FROM Categorias", cn);<br /><br /> cmd.CommandType = CommandType.Text;<br /><br /> cn.Open();<br /><br /> return GetCategoriaCollectionFromReader(ExecuteReader(cmd));<br /><br /> }<br /><br /> }<br /><br /> /// <summary><br /><br /> /// Retorna uma categoria existente com o ID especificado<br /><br /> /// </summary><br /><br /> public override CategoriaDetails GetCategoriaByID(int categoryID)<br /><br /> {<br /><br /> using (SqlConnection cn = new SqlConnection(this.ConnectionString))<br /><br /> {<br /><br /> SqlCommand cmd = new SqlCommand("tb_Categorias_GetCategoryByID", cn);<br /><br /> cmd.CommandType = CommandType.StoredProcedure;<br /><br /> cmd.Parameters.Add("@CategoryID", SqlDbType.Int).Value = categoriaid;<br /><br /> cn.Open();<br /><br /> IDataReader reader = ExecuteReader(cmd, CommandBehavior.SingleRow);<br /><br /> if (reader.Read())<br /><br /> return GetCategoriaFromReader(reader);<br /><br /> else<br /><br /> return null;<br /><br /> }<br /><br /> }<br /><br /> /// <summary><br /><br /> /// Deleta uma categoria<br /><br /> /// </summary><br /><br /> public override bool DeleteCategoria(int categoriaID)<br /><br /> {<br /><br /> using (SqlConnection cn = new SqlConnection(this.ConnectionString))<br /><br /> {<br /><br /> SqlCommand cmd = new SqlCommand("tb_Categorias_DeleteCategory", cn);<br /><br /> cmd.CommandType = CommandType.StoredProcedure;<br /><br /> cmd.Parameters.Add("@CategoriaID", SqlDbType.Int).Value = categoriaID;<br /><br /> cn.Open();<br /><br /> int ret = ExecuteNonQuery(cmd);<br /><br /> return (ret == 1);<br /><br /> }<br /><br /> }<br /><br /> /// <summary><br /><br /> /// Atualiza uma categoria<br /><br /> /// </summary><br /><br /> public override bool UpdateCategoria(CategoriaDetails categoria)<br /><br /> {<br /><br /> using (SqlConnection cn = new SqlConnection(this.ConnectionString))<br /><br /> {<br /><br /> SqlCommand cmd = new SqlCommand("tb_Categorias_UpdateCategory", cn);<br /><br /> cmd.CommandType = CommandType.StoredProcedure;<br /><br /> cmd.Parameters.Add("@CategoriaID", SqlDbType.Int).Value = categoria.categoriaid;<br /><br /> cmd.Parameters.Add("@Nome", SqlDbType.NVarChar).Value = categoria.nome;<br /><br /> cmd.Parameters.Add("@ImagemUrl", SqlDbType.NVarChar).Value = categoria.imagemurl;<br /><br /> cmd.Parameters.Add("@Descricao", SqlDbType.NVarChar).Value = categoria.descricao;<br /><br /> cn.Open();<br /><br /> int ret = ExecuteNonQuery(cmd);<br /><br /> return (ret == 1);<br /><br /> }<br /><br /> }<br /><br /> /// <summary><br /><br /> /// Cria um nova categoria<br /><br /> /// </summary><br /><br /> public override int InsertCategoria(CategoriaDetails categoria)<br /><br /> {<br /><br /> using (SqlConnection cn = new SqlConnection(this.ConnectionString))<br /><br /> {<br /><br /> SqlCommand cmd = new SqlCommand("tb_Categorias_InsertCategory", cn);<br /><br /> cmd.CommandType = CommandType.StoredProcedure;<br /><br /> //cmd.Parameters.Add("@CategoriaID", SqlDbType.Int).Value = categoria.categoriaid;<br /><br /> cmd.Parameters.Add("@Nome", SqlDbType.NVarChar).Value = categoria.nome;<br /><br /> cmd.Parameters.Add("@ImagemUrl", SqlDbType.NVarChar).Value = categoria.imagemurl;<br /><br /> cmd.Parameters.Add("@Descricao", SqlDbType.NVarChar).Value = categoria.descricao;<br /><br /> cmd.Parameters.Add("@CategoriaID", SqlDbType.Int).Direction = ParameterDirection.Output;<br /><br /> cn.Open();<br /><br /> int ret = ExecuteNonQuery(cmd);<br /><br /> return (int)cmd.Parameters["@CategoriaID"].Value;<br /><br /> }<br /><br /> }<br /><br />}Note que os métodos implementados na classe
concreta usam stored procedures com exceção do método GetCategorias(),
que utiliza um comando SQL em texto: SELECT * FROM Categorias;
public override List<CategoriaDetails> GetCategorias()<br /><br /> {<br /><br /> using (SqlConnection cn = new SqlConnection(this.ConnectionString))<br /><br /> {<br /><br /> SqlCommand cmd = new SqlCommand("SELECT * FROM Categorias", cn);<br /><br /> cmd.CommandType = CommandType.Text;<br /><br /> cn.Open();<br /><br /> return GetCategoriaCollectionFromReader(ExecuteReader(cmd));<br /><br /> }<br /><br /> }Os métodos GetCategoriaCollectionFromReader()
é herdado da classe CategoriaProvider, e o método
ExecuteReader() é herdado da classe DataAccess.
Para testar o nosso modelo vamos usar o método
GetCategorias() para exibir as categorias cadastradas no GridView na
página Default.aspx. Para isso vamos alterar o código do evento Load
da página Default.aspx.cs conforme abaixo:
protected void Page_Load(object sender, EventArgs e)<br /> {<br /> try<br /> {<br /> CategoriaDAL cat = new CategoriaDAL();<br /> gdvCategorias.DataSource = cat.GetCategorias();<br /> gdvCategorias.DataBind();<br /> }<br /> catch (Exception ex)<br /> {<br /> lblErro.Text = "Erro ao acessar dados : " + ex.Message;<br /> }<br /> }Tivemos que criar uma instância da classe concreta CategoriaDAL, visto que o método GetCategorias não é estático para,
em seguida, usar o método
GetCategorias(), que retorna uma lista de objetos
categorias.
Executando a página web iremos obter:

Com isso criamos uma camada de acesso a dados
usando os recursos da orientação a objeto onde temos realmente a reutilização de
código e a especialização das classes.
Eu sei, é apenas ASP .NET, mas eu
gosto…








