Simplificando o uso de tipos nulos em entidades em projetos OO – Parte 03

Veja neste ultimo artigo como ler entidades utilizando Reflection.

Simplificando o uso de tipos nulos em entidades em projetos OO – Parte 03

Lendo entidades utilizando Reflection

Escrever códigos para ler entidades diretas é uma tarefa muito cansativa. Para apressar o desenvolvimento e aliviar o aborrecimento, você pode combinar as classes Generics, Reflection e atributos customizados, e escrever um construtor universal de entidade e de lista de entidade.

A Listagem 8 contém uma classe de acesso a dados responsável apenas por construir entidades e listas de entidades, mas ela poderá construirá qualquer entidade. Se você externar a string de conexão e utilizar as interfaces de System.Data ADO.NET, este mesmo código construirá entidades e lista de entidades para praticamente qualquer banco de dados e qualquer tabela.

 

Listagem 8. Uma classe DataAccess genérica que utiliza Generics, atributos customizados e Reflection para ler quase todas as entidades

Public Class DataAccess

   Private Const connectionString As String = _

"Data Source=localhost;Initial Catalog=Northwind;" + _

"Integrated Security=True"

 

   Public Shared Function GetList(Of T As New, _

R As New)(ByVal tablename As String) As T

 

Using connection As SqlConnection = _

New SqlConnection(connectionString)

Dim command As SqlCommand = _

New SqlCommand("SELECT * FROM " + tablename, connection)

connection.Open()

Dim reader As SqlDataReader = command.ExecuteReader

Return Create(Of T, R)(reader)

End Using

 

   End Function

   Public Shared Function GetEmployeeList() As EmployeeList

Return GetList(Of EmployeeList, Employee)("Employees")

   End Function

 

   Private Shared Function Create(Of T As New, U As New)( _

ByVal reader As SqlDataReader) As T

 

Dim list As IList(Of U)

Dim gt As T = New T()

list = gt

 

While (reader.Read())

list.Add(Create(Of U)(reader))

End While

 

Return list

   End Function

 

   Private Shared Function Create(Of U As New)( _

ByVal reader As SqlDataReader) As U

 

Dim o As U = New U()

 

' get the attributes and use them to read

Dim type As Type = GetType(U)

Dim properties() As PropertyInfo = type.GetProperties()

 

' for each field if it as a field descriptor we can read it

For Each p As PropertyInfo In properties

 

Dim attributes() As Object = _

p.GetCustomAttributes(GetType(FieldDescriptorAttribute), _

 False)

 

For Each attr As Object In attributes

 

If (TypeOf attr Is FieldDescriptorAttribute) Then

Dim descriptor As FieldDescriptorAttribute = _

CType(attr, FieldDescriptorAttribute)

 

Dim method As MethodInfo = _

GetType(DataAccess).GetMethod("SafeRead")

method = method.MakeGenericMethod(descriptor.FieldType)

Dim val As Object = method.Invoke(Nothing, _

New Object() _

{descriptor.FieldName, reader, _

descriptor.DefaultValue})

p.SetValue(o, val, Nothing)

Exit For

 

End If

Next

Next

 

Return o

   End Function

 

   Public Shared Function SafeRead(Of T)(ByVal fieldname As String, _

ByVal defaultValue As T) As T

 

Dim v As Object = reader(fieldname)

If (v Is Nothing Or v Is System.DBNull.Value) Then

Return defaultValue

Else

Return Convert.ChangeType(v, GetType(T))

End If

   End Function

 

End Class

 

A primeira função, GetList, requer dois argumentos como parâmetros, T e R, que executam um construtor em branco, Sub Novo. T é o tipo de lista da entidade, tal como EmployeeList; U é o tipo de entidade, tal como Empregado. A função compartilhada GetList requer o nome da tabela para que faça a leitura.

O segundo método compartilhado, Create (Criar), organiza a construção da lista, iterando sobre o leitor e construindo as entidades que formam a lista. O terceiro método faz todo o trabalho pesado: ele cria uma instância de uma classe da entidade indicada pelo parâmetro U. Em seguida, você obtém todas as propriedades para a entidade e solicita o FieldDescriptorAttribute para elas. Para cada propriedade você utiliza o FieldDescriptorAttribute e constrói uma instância do método genérico SafeRead, utilizando o conhecimento do FieldDescriptorAttribute sobre cada campo para inicializá-lo.

Alguém vai escrever e falar sobre o desempenho com todo este uso de Reflection. Correto, o desempenho pode ser ligeiramente mais lento, mas hardware é barato, já o trabalho, não. Se você achar que o desempenho não é aquele que necessitaria ser, você sempre pode executar o código que não usa Reflection, mas certifique-se que o desempenho realmente importa tanto e se o uso de Reflection é realmente mais lento. Para testar o código, você pode usar a sub rotina Main, mostrada na Listagem 9.

 

Listagem 9. Um aplicativo simples de console para testar o código

Imports System.Data

Imports System.Data.SqlClient

Imports System.Threading

Imports System.Reflection

 

Module Module1

 

   Sub Main()

 

Dim emp As Employee = New Employee

emp.BirthDate = Nothing

 

Dim list As EmployeeList = DataAccess.GetEmployeeList

For Each e As Employee In list

Console.WriteLine(e)

Next

 

Console.ReadLine()

 

For Each  c As Customer In DataAccess.GetList( _

Of CustomerList, Customer)("Customers")

Console.WriteLine(c)

Next

 

Console.ReadLine()

 

   End Sub

... ' elided

 

Herdando a partir de List(Of T)

Não é necessário criar uma CustomerList (ListagemDeClientes) que herde a partir de List(Of Customer) ou criar nenhum subtipo. Uma razão para fazer isto é apoiar a adição de funcionalidades extras à nova classe List. Na programação real, você necessitará de métodos auxiliares, e estes podem ser adicionados somente às classes que você criar (ver Nota 3).

 

Nota 3. Métodos de Extensão no Orcas

Com o Orcas (a próxima versão do .NET), métodos de extensão são suportados. Os métodos de extensão permitem adicionar potencialidades a um tipo existente. Para esta finalidade, você pode ser capaz de utilizar List(of Customer), por exemplo, sem herança e adicionar novas potencialidades utilizando métodos de extensão.


Conclusão

Há muitos trechos importantes neste artigo. Você pode usar tipos nulos para fazer campos e fazer com que as propriedades aceitem o valor nulo.

É importante reconhecer que muitas pessoas utilizam ADO.NET e DataSets. Esta abordagem pode funcionar, mas eu a utilizo raramente. Prefiro o controle fornecido por classes customizadas.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados