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

Veja neste artigo como implementar um método SafeRead e como criar um atributo descritor de campos.

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

Implementando um método SafeRead(Of T)

As classes e os métodos genéricos (generics) são utilizados em todos os lugares em .NET. Classes Generics são muito úteis. A qualquer momento que você encontrar um algoritmo genérico, que trabalhe com uma variedade de possibilidades, utilize a classe Generics.

Em meu trabalho diário, rotineiramente utilizo macros, snippets, CodeRush, e geradores de CodeDOM. Estabelecendo um padrão para completar uma tarefa, torna-se mais fácil de automatizá-la ou, pelo menos, fazê-la muito rapidamente. Por esta razão, desenvolvi pessoalmente o método SafeRead (De T).

SafeRead é implementado para aceitar um tipo de dados, algum tipo do container ADO.NET tal como um DataRow, um nome de campo e um valor padrão. Então, SafeRead tenta ler o campo, contanto que este não seja nulo. Se o campo tiver um valor nulo, o valor padrão será usado. Apesar de um código parecido com o descrito na Listagem 4 ser repetido várias vezes, o código verificador é convertido em um único método e a carga de trabalho é bastante reduzida. A Listagem 5 contém a implementação de SafeRead (Of T).

 

Listagem 4. Exemplo de código que verifica se o valor de uma variável é nulo.

Dim v As Object = reader("EmployeeID")

Dim employeeID As Integer

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

   employeeID = Convert.ToInt32(v)

Else

   employeeID = -1    ' default value

End If

 

Listagem 5. Um método genérico denominado SafeRead que retira o trabalho bruto da leitura de valores de campos e da verificação por nulo.

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

   ByVal reader As SqlDataReader, 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

 

A primeira coisa a ser feita é ler o campo. No exemplo da Listagem 5, a partir de um leitor de dados em SQL (SqlDataReader). Você pode ainda estender o método SafeRead para aceitar um leitor do tipo IDataReader. Em seguida, você verifica se há valores nulos. Se o campo for alguma das variações do valor nulo, o valor padrão que você forneceu será retornado. Se as informações forem válidas, você utiliza Convert.ChangeType e o tipo T, passado como parâmetro, para converter a informação ao tipo de dados correto.

Vale a pena notar que o VB é menos rígido em relação aos tipos de dados, e assim você provavelmente pode escapar da necessidade de atribuir o tipo do Objeto de um SqlDataReader indexado para um tipo específico. Entretanto, é interessante notar que ser específico é uma maneira mais portátil e mais confiável de escrever o código. Para utilizar o SafeRead, chame-o com parâmetros que se assemelham ao código abaixo.

 

Dim employeeID As Integer = _

   SafeRead(Of Integer)("EmployeeID", reader, -1)

Criando um atributo descritor de campos

Suponha que você queira eliminar mais alguns overheads para a construção de tipos de entidade. Por exemplo, você poderia utilizar meta-dados para fornecer o nome do campo, o tipo do campo e o valor padrão. Então, o campo de cada entidade pode carregar com ele a informação que necessita para usar o método SafeRead. A Listagem 6 tem uma execução de um atributo customizado, denominado FieldDescriptorAttribute (AtributoDescritorDeCampo).

 

Listagem 6. Um atributo customizado que pode ser utilizado para auxiliar com a leitura de campos de entidades

<AttributeUsage(AttributeTargets.Property)> _

Public Class FieldDescriptorAttribute

   Inherits Attribute

 

   ''' <summary>

   ''' Initializes a new instance of the FieldDescriptorAttribute

   ''' class.

   ''' </summary>

   ''' <param name="fieldName"></param>

   ''' <param name="fieldType"></param>

   Public Sub New(ByVal fieldName As String, _

      ByVal fieldType As Type, ByVal defaultValue As Object)

      FFieldName = fieldName

      FFieldType = fieldType

      If (FFieldType Is GetType(DateTime)) Then

         FDefaultValue = DateTime.MinValue

      Else

         FDefaultValue = defaultValue

      End If

   End Sub

 

   Private FFieldName As String

   Public Property FieldName() As String

      Get

         Return FFieldName

      End Get

      Set(ByVal Value As String)

         FFieldName = Value

      End Set

   End Property

 

   Private FFieldType As Type

   Public Property FieldType() As Type

      Get

         Return FFieldType

      End Get

      Set(ByVal Value As Type)

         FFieldType = Value

      End Set

   End Property

 

   Private FDefaultValue As Object

   Public Property DefaultValue() As Object

      Get

         Return FDefaultValue

      End Get

      Set(ByVal Value As Object)

         FDefaultValue = Value

      End Set

   End Property

End Class

 

A classe do atributo customizado FieldDescriptorAttribute é simplesmente um recipiente para os argumentos a serem usados no método SafeRead. O único aspecto a ser notado é que eu verifico para o tipo DateTime e configuro o valor padrão no atributo customizado. Definido o FieldDescriptorAttribute, você pode determinar uma classe de entidade customizada (baseada em qualquer tabela, neste caso, Employee foi usada) e alinhar os campos de propriedade/entidade com o atributo (veja a Listagem 7).

 

Listagem 7. A classe Employee que utiliza o FieldDescriptorAttribute, que pode ser utilizada por SafeRead com Reflection

Public Class Employee

 

   Private FEmployeeID As Nullable(Of Integer)

   <FieldDescriptor("EmployeeID", GetType(Integer), -1)> _

   Public Property EmployeeID() As Nullable(Of Integer)

      Get

         Return FEmployeeID

      End Get

      Set(ByVal Value As Nullable(Of Integer))

         If (Value.HasValue = False) Then

            Console.WriteLine("Employee ID is null")

         End If

         FEmployeeID = Value

      End Set

   End Property

 

   Private FLastName As String

   <FieldDescriptor("LastName", GetType(String), "")> _

   Public Property lastName() As String

      Get

         Return FLastName

      End Get

      Set(ByVal Value As String)

         FLastName = Value

      End Set

   End Property

 

   Private FFirstName As String

   <FieldDescriptor("FirstName", GetType(String), "")> _

   Public Property FirstName() As String

      Get

         Return FFirstName

      End Get

      Set(ByVal Value As String)

         FFirstName = Value

      End Set

   End Property

 

   Private FBirthDate As Nullable(Of DateTime)

   <FieldDescriptor("BirthDate", GetType(DateTime), Nothing)> _

   Public Property BirthDate() As Nullable(Of DateTime)

      Get

         Return FBirthDate

      End Get

      Set(ByVal Value As Nullable(Of DateTime))

         FBirthDate = Value

      End Set

   End Property

 

   Private FPhoto As Byte()

   <FieldDescriptor("Photo", GetType(Byte()), New Byte() )> _

   Public Property Photo() As Byte()

      Get

         Return FPhoto

      End Get

      Set(ByVal Value As Byte())

         FPhoto = Value

      End Set

   End Property

 

   Public Overrides Function ToString() As String

      Return String.Format("Employee: , is in ", _

         FEmployeeID, FLastName, FFirstName, FRegion)

   End Function

 

   Private FRegion As String

   <FieldDescriptor("Region", GetType(String), "(unk)")> _

   Public Property Region() As String

      Get

         Return FRegion

      End Get

      Set(ByVal Value As String)

         FRegion = Value

      End Set

   End Property

 

End Class


Artigos relacionados