Criação das regras de utilização da classe DAL genérica:
Codificaremos agora dois projetos onde iremos implementar as regras para a utilização de nossa classe DAL genérica a ser construida ainda.
B) Criar um projeto chamado BaseClasses e incluir uma classe que herde da Interface IBaseModel e implemente seu método de verificação de uso dos atributos;
C) Na classe DAL genérica, estabelecer que a o objeto que a está consumindo, utilize um tipo que implemente nossa interface, pois deste modo, poderemos chamar o método de validação via reflection, e assim verificar se nossos atributos estão sendo utilizados e se estão sendo utilizados de maneira correta.
Vamos
começar pela nossa Interface. Vamos criar mais um projeto Class Library de nome
Interfaces. Após criá – lo, apague a classe padrão que o Visual Studio cria
automaticamente e adicione uma Interface, clicando com o botão direito em cima
do projeto Interface e clique em Add New Item. O código da interface esta a
seguir :
using System;
namespace GenDAL.Library.Interfaces
{
public interface IBaseModel
{
void ValidateUsageCustomAttr();
}
}
Como podemos verificar, essa Interface é bastante simples. Ela declara apenas um método que precisa ser implementado pela classe que a herdar.
Agora
vamos ao projeto BaseClasses. Este projeto terá apenas uma classe por enquanto,
que herdará nossa interface e implementará nosso método de validação.
Primeiramente vamos dar um overview pela classe:

Ilustração 8
- Estrutura da classe "BaseClassModel"
Como podemos verificar nas cláusulas “using” vamos utilizar objetos do .Net e as Dlls de nossos projetos “Attributes” e “Interfaces”, portanto é necessário referenciá-las no projeto.
Definimos que esta classe será abstrata, ou seja, nunca possuirá instâncias e definirá um modelo para uma funcionalidade – no caso a validação de uso dos atributos customizados.
Podemos observar também que ela herda da Interface que criamos, nos obrigando a implementar nosso método de validação.
Dividimos o código em 3 “regions” – Exception Class, Private Methods e IBaseModelMembers.
Vamos iniciar a codificação pela region “Exception Class”, que conterá a uma classe especializada de exceção:
#region Exception Class
public class GenDALValidateModelClassException : Exception
{
public string ErrorMessage
{
get { return base.Message; }
}
public GenDALValidateModelClassException(string errorMessage) : base(errorMessage)
{
}
public GenDALValidateModelClassException(string errorMessage, Exception innerException)
: base(errorMessage, innerException)
{
}
}
#endregion
Explicando rapidamente, criamos uma classe que herda de System.Excepetion e utiliza a propriedade Message e dois de seus construtores.
Agora vamos criar a region “Private Methods”
O primeiro método que iremos ver , é o método que valida o uso do atributo AttDataAccessType, ou seja o atributo que informa se a classe model representa uma tabela no BD, uma SP ou ambos.
private bool ValidateAttDataAccesType(out DataAccesType dataAccesType)
{
bool retorno = false;
DataAccesType type = DataAccesType.None;
AttDataAccessType att = null;
try
{
try
{
att = (AttDataAccessType)this.GetType().GetCustomAttributes(typeof(AttDataAccessType), false)[0];
type = att.DataAccesType;
retorno = true;
}
catch { }
if (type == DataAccesType.None)
{
throw new GenDALValidateModelClassException("The attribute GenDAL.Library.Attributes.AttDataAccesType was not found in the current Model class");
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
att = null;
}
dataAccesType = type;
return retorno;
}
Este método retorna um bool e retorna também num parâmetro out o enum DataAccesType.
Inicialmente declaramos algumas variáveis:
bool retorno = false;
DataAccesType type = DataAccesType.None;
AttDataAccessType att = null;
Depois
criamos dois blocos “try catch”, e dentro deles utilizamos reflection para
realizar a busca de atributos
customizados. Com isso verificamos se existem atributos customizados e se um
desses atributos é do tipo que queremos – AttDataAccessType:
att = (AttDataAccessType)this.GetType().GetCustomAttributes(typeof(AttDataAccessType), false)[0];
O
método this.GetType().GetCustomAttributes(typeof(AttDataAccessType),
false) retorna um array de objetos. Como queremos verificar a existência
de um único atributo, posicionamos o retorno deste método para o primeiro
índice do array e fazemos o parse deste objeto de retorno.
Com isso podemos atribuir um valor ao parâmetro de saida e validar o uso do nosso atributo:
type = att.DataAccesType;
retorno = !type.Equals(DataAccesType.None);
Caso a propriedade DataAccesType do atributo estaja setada para “none”, disparamos uma exceção do tipo GenDALValidateModelClassException que criamos acima
Os próximos métodos privados, validam o uso dos atributos AttFieldDB , AttParamProcDB e AttProcNameSelectNoFilter:
private bool ValidateAttParamProcDB(PropertyInfo piInfo)
{
AttParamProcDB att = null;
try
{
att = piInfo.GetCustomAttributes(typeof(AttParamProcDB), false)[0] as AttParamProcDB;
}
catch { }
return (att != null);
}
private bool ValidateAttFieldDB(PropertyInfo piInfo)
{
AttFieldDB att = null;
try
{
att = piInfo.GetCustomAttributes(typeof(AttFieldDB), false)[0] as AttFieldDB;
}
catch { }
return (att != null);
}
private bool ValidateAttProcNameSelectNoFilter(DataAccesType dataAccesType)
{
bool retorno = true;
try
{
try
{
if (dataAccesType == DataAccesType.StoredProcedure || dataAccesType == DataAccesType.Both)
{
retorno = (AttProcNameSelectNoFilter)this.GetType().GetCustomAttributes(typeof(AttProcNameSelectNoFilter), false)[0] != null;
}
}
catch { retorno = false; }
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
return retorno;
}
Os
dois primeiros métodos recebem um PropertyInfo, um objeto de System.reflection
que permite descobrir os atributos da propriedade refletida, bem como ter
acesso aos metadados destas propriedades. Com o PropertyInfo verificamos se a
propriedade da classe model está usando o atributo que queremos.
O terceiro método recebe a enumeração DataAccesType
e verifica se o
atributo AttProcNameSelectNoFilter está sendo usado na classe model
corrente, caso a enumeração seja do tipo DataAccesType.StoredProcedure
ou DataAccesType.Both.
O próximo método que criaremos, é o método publico que fará efetivamente a validação do uso dos atributos:
public void ValidateUsageCustomAttr()
{
Dictionary < string, string[] > retornoOut = null;
DataAccesType daType = DataAccesType.None;
try
{
if (this.ValidateAttDataAccesType(out daType))
{
retornoOut = new Dictionary < string, string[] >();
if (!this.ValidateAttProcNameSelectNoFilter(daType))
{
retornoOut.Add("", new string[] { "GenDAL.Library.Attributes.AttProcNameSelectNoFilter" });
}
foreach (PropertyInfo piAux in this.GetType().GetProperties())
{
switch (daType)
{
case DataAccesType.StoredProcedure | DataAccesType.Both:
if (!this.ValidateAttParamProcDB(piAux) && !this.ValidateAttFieldDB(piAux) )
{
retornoOut.Add(piAux.Name, new string[] { "GenDAL.Library.Attributes.AttParamProcDB", "GenDAL.Library.Attributes.AttFieldDB"});
}
else
{
if (!this.ValidateAttParamProcDB(piAux))
{
retornoOut.Add(piAux.Name, new string[] { "GenDAL.Library.Attributes.AttParamProcDB" });
}
if (!this.ValidateAttFieldDB(piAux))
{
retornoOut.Add(piAux.Name, new string[] { "GenDAL.Library.Attributes.AttFieldDB" });
}
}
break;
case DataAccesType.DirectTableAcces:
if (!this.ValidateAttFieldDB(piAux))
{
retornoOut.Add(piAux.Name, new string[] { "GenDAL.Library.Attributes.AttFieldDB" });
}
break;
}
}
if (retornoOut != null && retornoOut.Count > 0)
{
throw new GenDALValidateModelClassException(this.GetErrorMessage(retornoOut));
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
}
Primeiramente,
verificamos se a classe utliza o parâmetro AttDataAccessType, utilizando o método privado “ValidateAttDataAccesType”.
Este método além de
validar, ele nos fornece a informação de qual enumeração DataAccesType
está sendo utilizada
através de seu parâmetro out. Com essa informação podemos implementar regras
diferentestes para cada situação.
Depois
iniciamos um Dictionary que será o responsável por armazenar as informações de
erro personalizadas para a nossa exception.
Logo
após, fazemos uma varredura de todas as propriedades da classe corrente,
utilizando método GetType.GetProperties() de System.Reflection :
foreach (PropertyInfo piAux in this.GetType().GetProperties()).
Dentro do
foreach, verificamos qual o DataAccesType da classe. Se for StoredProcedure ou
Both, obrigamos o uso dos atributos AttParamProcDB e
AttFieldDB. Se for DirectTableAcces
obrigamos o uso do atributo AttFieldDB
apenas. Caso uma ou
mais propriedade não for validada, será disparada uma exceção do tipo GenDALValidateModelClassException
contendo como mensagem,
quais propriedades não foram validadas e quais os atributos esperados. Para a
composição desta mensagem, utilizamos o método privado a seguir:
private string GetErrorMessage(Dictionary < string, string[] > dic)
{
StringBuilder sbProp = new StringBuilder();
try
{
sbProp.AppendLine("Some of the Custom Attributes requireds was not found in the properties:");
foreach (KeyValuePair<string, string[]> val in dic)
{
sbProp.Append(String.Format("Property : {0} ==> Attribute(s) : ", val.Key));
if (val.Value.Length > 0)
{
// sbProp.Append(" Custom Attribute not found : ");
foreach (string str in val.Value)
{
sbProp.Append(str + ", ");
}
if (sbProp.ToString().EndsWith(", "))
{
sbProp.Remove(sbProp.ToString().Length - 2, 2);
}
sbProp.AppendLine(" ");
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
//sbProp = null;
}
return sbProp.ToString();
}
Com isso terminamos esses dois projetos.
Algumas coisas podemos melhorar como a colocação das strings que representam as mensagens de erro em arquivos xml ou de configuração por exemplo. Porém para maior agilidade no desenvolvimento deste artigo optamos por deixar no código.
Nesses
dois projetos, vimos algumas funcionalidades de Reflection, como a busca das
propriedades de uma classe em tempo de execução e a varredura em atributos
customizados, tanto da classe como de propriedades da classe. Essas são
funcionalidades básicas de reflection, mas que podem ser de uma utilidade
enorme se bem utilizadas.