<< Click to Display Table of Contents >> Usando Reflection, Generics e Custom Attributes para montar SQL |
![]() ![]() ![]() |
Custom Attributes
Com Custom Attributes podemos criar anotações sobre o nome de uma classe e tabela para indicar qual campo as propriedades da classe se referem
Cliente.cs
[Tabela("TB_CLIENTE")]
public class Cliente
{
[Campo("CD_CLIENTE", Chave = true)]
public string Id { get; set; }
[Campo("NM_CLIENTE")]
public string Nome { get; set; }
[Campo("VL_LIMITE")]
public string Limite { get; set; }
[Campo("CD_CIDADE")]
public string IdCidade { get; set; }
}
Para que o C# reconheça os atributos são necessário criar classes com sufixo Attributes:
Atributos.cs
// classe auxiliar para tributo tabela
public class TabelaAttribute : Attribute
{
public TabelaAttribute(string nome)
{
this.Nome = nome;
}
public string Nome { get; set; }
}
// class auxiliar para tributo campo
public class CampoAttribute : Attribute
{
public CampoAttribute(string nome)
{
this.Nome = nome;
}
public string Nome { get; set; }
public bool Chave { get; set; }
}
Projeto principal
No projeto principal, basta chamar funções auxiliares:
Program.cs
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Cliente cliente = new Cliente() { Id = "10", Nome = "Junior", IdCidade = "10", Limite = "1000.00"};
Console.WriteLine(Auxiliar.MontaUpdate(cliente, typeof(Cliente)) + "\n");
Console.WriteLine(Auxiliar.MontaInsert(cliente, typeof(Cliente))");
Console.ReadKey();
}
}
}
Saída do programa
update TB_CLIENTE set NM_CLIENTE=Junior,VL_LIMITE=1000.00,CD_CIDADE=10 where CD_CLIENTE=10
insert into TB_CLIENTE (CD_CLIENTE,NM_CLIENTE,VL_LIMITE,CD_CIDADE) values (10,Junior,1000.00,10)
Isso pode ser feito com qualquer classe que for criada, basta criar os Custom Attributes.
Classes auxiliares com Reflection e Generics
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace ConsoleApplication1
{
// classe campo é usado para manter informações
// de todos os campos e valores deles num list
class Campo
{
public string Nome { get; set; }
public string Propriedade { get; set; }
public bool Chave { get; set; }
public string Tipo { get; set; }
public string Valor { get; set; }
}
/// <summary>
/// Classe auxilia a construção de insert/updates
/// Para usar basta chamar o método MontarInsert e MontarUpdate
/// </summary>
static class Auxiliar
{
/// <summary>
/// procura nos atributos do tipo ex: typeof(Pessoa)
/// um atributo do tipo "TabelaAtributo"
/// retorna o nome da tabela se achar
/// </summary>
/// <param name="tipo"></param>
/// <returns></returns>
private static string GetTabela(Type tipo)
{
// usa reflection para extrair os "Custom" atributes
Attribute[] atributos = Attribute.GetCustomAttributes(tipo);
// percorre todos atributos
foreach (Attribute atributo in atributos)
{
// achou atributo tabela? é esse!
if (atributo is TabelaAttribute)
{
// retorna no nome
TabelaAttribute tabela = (TabelaAttribute)atributo;
return tabela.Nome;
}
}
return "";
}
/// <summary>
/// método que recebe um tipo
/// pega os membros desse tipo e lê atributos custom
/// para retornar o nome do campo (retorna num list de campo)
/// </summary>
private static List<Campo> GetCampos(Type tipo)
{
List<Campo> retorno = new List<Campo>();
// reflection que pega os membros de um tipo
MemberInfo[] members = tipo.GetMembers();
// pra cada membro...
foreach (MemberInfo member in members)
{
// pega os atributos dos membros e joga num object[]
object[] attributes = member.GetCustomAttributes(true);
// se o membro for do tipo Property (pode ser Method, Public,)
if (member.MemberType == MemberTypes.Property)
{
// tem atributos?
if (attributes.Length != 0)
{
// percorre os atributos
foreach (object attribute in attributes)
{
// se o atributo for CampoAttribute...
if (attribute is CampoAttribute)
{
CampoAttribute da = (CampoAttribute)attribute;
// adiciona numa list um "Campo" que contém
// nome da pripriedade e do campo
Campo campo = new Campo()
{
Chave = da.Chave,
Nome = da.Nome,
Propriedade = member.Name.ToString()
};
retorno.Add(campo);
}
}
}
}
}
return retorno;
}
// este método aceita como parâmetro Generics
// ele pega as propriedades do objeto t e as percorre
// pra cada propriedade, ele procura na list passada como parâmetro
// um campo com mesma "Propriedade" (que é o nome do campo)
// se achou, adiciona o valor na propria list que é ref e cai fora
private static void GetPropriedades<T>(T t, ref List<Campo> lista)
{
Type tipo = typeof(T);
// pega as propriedades e os valores do objeto
var properties = tipo.GetProperties();
// percorre as propriedades
foreach (PropertyInfo property in properties)
{
// pra cada propriedade, procura o equivalente no "lista"
// que já contem o nome e tipo do campo
for (int i = 0; i < lista.Count; i++)
{
// encontrou com mesmo nome?
if (lista[i].Propriedade.Equals(property.Name))
{
lista[i].Valor = property.GetValue(t, null).ToString();
break;
}
}
}
}
/// <summary>
/// Função retorna uma string já montadinha de update com os campos e nome
/// da tabela com base no custom attributes feitos no objeto "t"
/// </summary>
/// <typeparam name="T">Classe do modelo</typeparam>
/// <param name="t">Objeto (pessoa, cliente, etc)</param>
/// <param name="tipo">typeof(t)</param>
/// <returns>um string com update já levando em conta as chaves</returns>
public static string MontaUpdate<T>(T t, Type tipo)
{
// o GetCampos retorna usando reflection o nome dos campos
List<Campo> campos = GetCampos(tipo);
// GetPropriedades completa a list campos com o valor dos campos
GetPropriedades(t, ref campos);
StringBuilder chavestr = new StringBuilder();
StringBuilder campostr = new StringBuilder();
// percorre os campos chaves e normais
foreach (Campo campo in campos)
{
if (campo.Chave)
chavestr.Append(campo.Nome + "=" + campo.Valor + " and ");
else
campostr.Append(campo.Nome + "=" + campo.Valor + ",");
}
// remove lixo no final
chavestr.Remove(chavestr.Length - 5, 5);
campostr.Remove(campostr.Length - 1, 1);
// monta update de saída
return "update " + GetTabela(tipo) +
" set " + campostr.ToString() +
" where " + chavestr.ToString();
}
/// <summary>
/// Função retorna uma string já montadinha de insert com os campos e nome
/// da tabela com base no custom attributes feitos no objeto "t"
/// </summary>
/// <typeparam name="T">Classe do modelo</typeparam>
/// <param name="t">Objeto (pessoa, cliente, etc)</param>
/// <param name="tipo">typeof(t)</param>
/// <returns>um string com insert já levando em conta as chaves</returns>
public static string MontaInsert<T>(T t, Type tipo)
{
// o GetCampos retorna usando reflection o nome dos campos
List<Campo> campos = GetCampos(tipo);
// GetPropriedades completa a list campos com o valor dos campos
GetPropriedades(t, ref campos);
StringBuilder valuesstr = new StringBuilder();
StringBuilder campostr = new StringBuilder();
// percorre os campos e valores
foreach (Campo campo in campos)
{
campostr.Append(campo.Nome + ",");
valuesstr.Append(campo.Valor + ",");
}
// remove lixo no final
valuesstr.Remove(valuesstr.Length - 1, 1);
campostr.Remove(campostr.Length - 1, 1);
// monta insert de saída
return "insert into " + GetTabela(tipo) +
" (" + campostr.ToString() +
") values (" + valuesstr.ToString() + ")";
}
}
}