Criando List<> dinamicamente a partir de Select, com Reflection
Finalizando a série Reflection na Prática: Uso na Classe de Conexão, mostro a vocês como criar um List<> de uma classe que implementa uma tabela do banco de dados a partir de um comando Select.
Usamos List<> geralmente para poder iterar com várias instâncias de um objeto, ou para popular uma lista (grids, etc), e sendo assim fica mais simples de trabalhar os dados em uma aplicação.
Vamos ver agora o método CreateList() da classe Conexão:
1: protected object CreateList(Type contentType, Type ListType, string isql, bool isqlHaveParams)
2: {
3: object list = Activator.CreateInstance(ListType);
4: DataTable dt = getTable(isql, isqlHaveParams);
5: foreach (DataRow r in dt.Rows)
6: {
7: object item = Activator.CreateInstance(contentType);
8: foreach (DataColumn dc in dt.Columns)
9: {
10: FieldInfo fiItens = contentType.GetField("_" + dc.ColumnName.ToUpper(), BindingFlags.NonPublic | BindingFlags.Instance);
11: if (r[dc.ColumnName] != DBNull.Value)
12: {
13: fiItens.SetValue(item, ConverterDB2Obj(r[dc.ColumnName], fiItens.FieldType));
14: }
15: }
16: ListType.InvokeMember("Add", BindingFlags.InvokeMethod, null, list, new object[] { item });
17: }
18: return list;
19: }
Este método é o mais curto de todos os outros mostrados, porém ele tem um conceito a mais: criação de objetos em tempo de execução.
Este método recebe como parâmetros o tipo base de cada item da lista, o tipo da lista em si, a instrução SQL e um indicador de parametrização desta instrução SQL.
A classe de cada tipo da lista deve ter as suas variáveis privadas nomeadas segundo a convenção do método Select(), e os campos informados na instrução select sm “isql” são os que serão atribuídos em cada propriedade de cada item do list.
Primeiramente, criamos uma instância do List<> que iremos retornar, porém não podemos fazer isso de forma hardcoded, pois como este método é genérico, serve para List<> de qualquer tipo.
Para contornar isso, criaremos este objeto em tempo de execução, e para isso utilizamos o método Activator.CreateInstance(), do namespace System.Runtime.Remoting, sendo que a classe Activator serve exatamente para esta criação de objetos em tempo de execução (runtime).
O método CreateInstance() requer como parâmetro na sobrecarga que utilizamos o tipo de objeto a ser criado, sendo o tipo no parâmetro ListType, que é o tipo do List<> que iremos criar será passado como esse parâmetro. Guardamos este objeto na variável “list”, do tipo object.
Em seguida, utilizamos o método getTable da classe Conexão, passando a instrução SQL para pegar os dados e guardamos no DataTable “dt”.
Com um laço foreach, percorremos cada registro do DataTable.
Dentro deste laço, criamos, utilizando novamente o método Activator.CreateInstance() uma instância do item da lista, cuja classe está no parâmetro “contentType”.
Aqui um parêntese: O que aconteceria se criássemos esta instância FORA do laço foreach, hein? ;-)
Agora faremos um outro laço foreach, agora para iterar sobre as colunas deste registro que estamos trabalhando. Guardamos na variável fiItens, do tipo FieldInfo, as informações sobre a variável privada associada àquele campo, com o método GetField() da variável “contentType”.
Se o campo não estiver nulo, atribuiremos este valor na propriedade utilizando o método SetValue() do FieldInfo “fiItens”, que recebe como parâmetro o item que instanciamos acima e o valor com uma rotina de conversão aplicada (ver os outros artigos desta série para entender melhor).
Atribuídos todos os campos, chamaremos o método Add() do List<> de uma forma diferente: Através do método InvokeMember da classe Type, que no nosso caso é representado pela variável “ListType”.
Ele recebe como parâmetros o nome do método, no nosso caso Add, o Binding Flag, que no nosso caso é BindingFlags.InvokeMethod, o binder padrão, que pode ser null, o objeto a sofrer a execução do método e um array de object que representa a lista de parâmetros do método a ser executado.
No nosso caso, criamos um array de object com apenas um elemento, cujo valor é a variável item, que é o item da lista que iremos inserir.
Iterados todos os registros, saímos do primeiro laço e retornamos o List<> na forma de object.
Veja como utilizar este método:
1: private List<TTpFormaContato> ListaGenerica()
2: {
3: List<TTpFormaContato> l = new List<TTpFormaContato>();
4: return (List<TTpFormaContato>)CreateList(this.GetType(), l.GetType(), _SearchSQL, CountSQLParams() > 0);
5: }
6:
7: public static List<TTpFormaContato> ListarTodos()
8: {
9: TTpFormaContato fc = new TTpFormaContato();
10: fc._SearchSQL = "select * from TP_FORMA_CONTATO";
11: fc.ClearSQLParams();
12: return fc.ListaGenerica();
13: }
Este trecho foi retirado do Simple PIM, o exemplo da nova Classe de Conexão, e de início criamos um objeto List<nome da classe> para poder usar seu método GetType() no método CreateList().
Para utilizar efetivamente o list retornado, fazemos um typecast para o tipo base desejado (o que instanciamos acima).
Enfim, chegamos ao final da série Reflection na Prática: Uso na Classe de Conexão e vimos como o Reflection pode fazer a gente economizar código :-)
Um abraço!
0 comentários:
Postar um comentário