Ir para o conteúdo

Como criar uma classe base para facilitar o acesso a dados via ADO.NET Entity Framework

Este artigo irá mostrar como construir uma base de código para facilitar a utilização de operações CRUD utilizando ADO.NET Entity Framework.

O nome do Modelo utilizado é ArtigosWikiEntities e o nome da tabela utilizada nos exemplos é Artigos.

Recursos utilizados:

  • .Net Framework 4.0
  • Um modelo Entity Framework

Criação das Classes

O primeiro passo é a criação de duas classes semelhantes, uma para efectuar operações directas e outra para efectuar operações utilizando uma transacção.

Para operações directas

Public Class Base
    'Implementação da interface IDisposable para permitir o uso do "Using"
    Implements IDisposable

    Private _Ent As ArtigosWikiEntities

    'Cria um nova instância do modelo quando se define uma nova instância desta classe
    Public Sub New()
        _Ent = New ArtigosWikiEntities
    End Sub

    ''' <summary>
    ''' Devolve uma instância do Modelo
    ''' </summary>
    ''' <value></value>
    ''' <returns>Modelo</returns>
    ''' <remarks></remarks>
    ''' Esta propriedade serve para caso o programador não queira utilizar nenhuma das funções disponíveis,
    ''' continue a ter acesso directo ao modelo se estar a definir novas variáveis
    Public ReadOnly Property Entities As ArtigosWikiEntities
        Get
            Return _Ent
        End Get
    End Property

    ''' <summary>
    ''' Devolve todos os registos encontrados na tabela indicada
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo)</typeparam>
    ''' <returns>Consulta em memoria</returns>
    ''' <remarks></remarks>
    ''' Esta é uma simples função onde apenas é necessário indicar o nome da classe onde
    ''' a função ira buscar os registos
    Public Function Obter(Of Tabela As Class)() As IQueryable(Of Tabela)
        Return _Ent.CreateObjectSet(Of Tabela)() 'Cria e retorna um novo objecto da classe seleccionada
    End Function

    ''' <summary>
    ''' Devolve todos os registos encontrados e filtrados na tabela indicada
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo)</typeparam>
    ''' <param name="Filtro">Filtro a aplicar</param>
    ''' <returns>Consulta em memoria</returns>
    ''' <remarks></remarks>
    ''' Esta função é um Overload da anterior em que já é possível utilizar um filtro
    Public Function Obter(Of Tabela As Class)(ByVal Filtro As Expressions.Expression(Of Func(Of Tabela, Boolean))) As IQueryable(Of Tabela)
        Return _Ent.CreateObjectSet(Of Tabela)().Where(Filtro)
    End Function

    ''' <summary>
    ''' Confirma as alterações pendentes
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função deve ser executada sempre que se efectue uma alteração
    ''' fora das funções disponíveis
    Public Function Guardar() As Boolean
        _Ent.SaveChanges() 'Guarda todas as alterações pendentes
        Return True
    End Function

    ''' <summary>
    ''' Adiciona um Registo
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo) onde o registo irá ser inserido</typeparam>
    ''' <param name="Classe">Instância criada e com os campos necessários preenchidos</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função serve para adicionar um novo registo onde é necessário indicar a classe de destino e também é necessário indicar a classe previamente criada
    Public Function Adicionar(Of Tabela As Class)(ByVal Classe As Tabela) As Boolean
        _Ent.CreateObjectSet(Of Tabela).AddObject(Classe) 'Cria um novo objecto da classe indicada e em seguida adiciona o novo registo
        _Ent.SaveChanges() 'Confirma a alteração
        Return True
    End Function

    ''' <summary>
    ''' Apaga um Registo
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo) onde o registo irá ser eliminado</typeparam>
    ''' <param name="Filtro">Filtro a implementar</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função serve para eliminar um registo onde é necessário indicar a classe de destino e também um filtro de modo a que um único registo seja encontrado
    Public Function Apagar(Of Tabela As Class)(ByVal Filtro As Expressions.Expression(Of Func(Of Tabela, Boolean))) As Boolean
        Dim Aux As Tabela 'Cria uma váriavel para prevenir um erro caso o código executado em baixo, não retorne qualaquer valor
        Aux = _Ent.CreateObjectSet(Of Tabela)().Where(Filtro).FirstOrDefault()
        If Aux IsNot Nothing Then
            _Ent.CreateObjectSet(Of Tabela).DeleteObject(Aux) 'Caso seja encontrado um registo vai apagar
            _Ent.SaveChanges() 'Confirma a alteração
            Return True 'Informa que o registo existia, por isso foi apagado
        Else
            Return False 'Informa que o registo não doi encontrado, por isso não foi eliminado
        End If
    End Function

    'Suporte para a interface IDisposable
#Region "IDisposable Support"
    Private disposedValue As Boolean

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                _Ent = Nothing
            End If
        End If
        Me.disposedValue = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

Para operações que necessitem de uma transacção

'Nota:
'Esta classe deve ser usada apenas na ocasião, ou seja, normalmente declara-se e utiliza-se
'dentro de um procedimento/função.
'Nunca deve ser declarada uma nova instância na parte publica
Public Class TBase
    'Implementação da interface IDisposable para permitir o uso do "Using"
    Implements IDisposable

    Private Tr As EntityClient.EntityTransaction
    Private _Ent As ArtigosWikiEntities

    ''' <summary>
    ''' Abre uma nova transacção
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Abre a conecção e transacção para se poder trabalhar
    Public Function Abrir() As Boolean
        _Ent.Connection.Open()
        Tr = CType(_Ent.Connection.BeginTransaction(), EntityClient.EntityTransaction)
        Return True
    End Function

    ''' <summary>
    ''' Confirma todas as operações efectuadas
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função deve ser executada no final de todas as operações, 
    ''' confirmando definitivamente as alterações.
    ''' Depois de executada já não ha maneira de voltar atrás
    Public Function Confirmar() As Boolean
        Tr.Commit()
        Return True
    End Function

    ''' <summary>
    ''' Caso dê erro, cancela todas as operações efectuadas
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função cancela todas as operações efectuadas até ao momento.
    ''' Normalmente deve ser executada quando ocorre um erro, para evitar a desorganização de informação
    Public Function Cancelar() As Boolean
        Tr.Rollback()
        Return True
    End Function

    ''' <summary>
    ''' Fecha a transacção actual
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Uma vez aberta, a conexão deve ser sempre fechada
    Public Function Fechar() As Boolean
        _Ent.Connection.Close()
        Return True
    End Function

    ''' <summary>
    ''' Devolve uma instância do Modelo
    ''' </summary>
    ''' <value>Modelo</value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta propriedade serve para caso o programador não queira utilizar nenhuma das funções disponíveis,
    ''' continue a ter acesso directo ao modelo se estar a definir novas variáveis
    Public ReadOnly Property Entities As ArtigosWikiEntities
        Get
            Return _Ent
        End Get
    End Property

    ''' <summary>
    ''' Devolve todos os registos encontrados na tabela indicada
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo)</typeparam>
    ''' <returns>Consulta em memoria</returns>
    ''' <remarks></remarks>
    ''' Esta é uma simples função onde apenas é necessário indicar o nome da class onde
    ''' a função ira buscar os registos
    Public Function Obter(Of Tabela As Class)() As IQueryable(Of Tabela)
        Return _Ent.CreateObjectSet(Of Tabela)() 'Cria e retorna um novo objecto da classe selecionada
    End Function

    ''' <summary>
    ''' Devolve todos os registos encontrados e filtrados na tabela indicada
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo)</typeparam>
    ''' <param name="Filtro">Filtro a aplicar</param>
    ''' <returns>Consulta em memoria</returns>
    ''' <remarks></remarks>
    ''' Esta função é um Overload da anterior em que já é possivel utilizar um filtro
    Public Function Obter(Of Tabela As Class)(ByVal Filtro As Expressions.Expression(Of Func(Of Tabela, Boolean))) As IQueryable(Of Tabela)
        Return _Ent.CreateObjectSet(Of Tabela)().Where(Filtro)
    End Function

    ''' <summary>
    ''' Confirma as alterações pendentes
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função deve ser executada sempre que se efectue uma alteração
    ''' fora das funções disponiveis
    Public Function Guardar() As Boolean
        _Ent.SaveChanges() 'Guarda todas as alterações pendentes
        Return True
    End Function

    ''' <summary>
    ''' Adiciona um Registo
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo) onde o registo irá ser inserido</typeparam>
    ''' <param name="Classe">Instância criada e com os campos necessários preenchidos</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função serve para adicionar um novo registo onde é necessario indicar a classe de destino e tambem é necessario indicar a classe previamente criada
    Public Function Adicionar(Of Tabela As Class)(ByVal Classe As Tabela) As Boolean
        _Ent.CreateObjectSet(Of Tabela).AddObject(Classe) 'Cria um novo objecto da classe indicada e em seguida adiciona o novo registo
        _Ent.SaveChanges() 'Confirma a alteração
        Return True
    End Function

    ''' <summary>
    ''' Apaga um Registo
    ''' </summary>
    ''' <typeparam name="Tabela">Nome da tabela(Classe existente no Modelo) onde o registo irá ser eliminado</typeparam>
    ''' <param name="Filtro">Filtro a implementar</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' Esta função serve para eliminar um registo onde é necessario indicar a classe de destino e tambem um filtro de modo a que um unico registo seja encotrado
    Public Function Apagar(Of Tabela As Class)(ByVal Filtro As Expressions.Expression(Of Func(Of Tabela, Boolean))) As Boolean
        Dim Aux As Tabela 'Cria uma váriavel para prevenir um erro caso o código executado em baixo, não retorne qualaquer valor
        Aux = _Ent.CreateObjectSet(Of Tabela)().Where(Filtro).FirstOrDefault()
        If Aux IsNot Nothing Then
            _Ent.CreateObjectSet(Of Tabela).DeleteObject(Aux) 'Caso seja encontrado um registo vai apagar
            _Ent.SaveChanges() 'Confirma a alteração
            Return True 'Informa que o registo existia, por isso foi apagado
        Else
            Return False 'Informa que o registo não foi encontrado, por isso não foi eliminado
        End If
    End Function

    'Suporte para a interface IDisposable
#Region "IDisposable Support"
    Private disposedValue As Boolean

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                _Ent = Nothing
                Tr = Nothing
            End If
        End If
        Me.disposedValue = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

Como Usar?

O uso é relativamente simples, seguem-se alguns exemplos:

Obter todos os registos

Using Ent As New Base
    Dgv.DataSource = Ent.Obter(Of Artigos).ToList() 'Vai buscar todos os registos da tabela "Artigos" e coloca-os num DataGridView
End Using

Obter o TOP 10

Using Ent As New Base
    Dgv.DataSource = Ent.Obter(Of Artigos).Take(10).ToList()
End Using

Obter todos os registos em que o tipo é igual a 1

Using Ent As New Base
    Dgv.DataSource = Ent.Obter(Of Artigos)(Function(A) A.Tipo = 1).ToList() 'Vai buscar todos os registos da tabela "Artigos" em que o Tipo é igual a 1 e coloca-os num DataGridView
End Using

Obter a descrição do artigo com o ID "Disco001"

Using Ent As New Base
    TextBox1.Text = Ent.Obter(Of Artigos)(Function(A) A.ID = "Disco001").FirstOrDefault().Descr 'Secciona o registo com o ID "Disco001" e vai buscar apenas a descrição e coloca-a numa textbox
End Using

Inserir um registo

Using Ent As New Base
    Dim _Artigos As New Artigos 'Define uma nova classe para depois ser parametrizada na função adicionar
    _Artigos.ID = "Disco001"
    _Artigos.Descr = "Disco Hitachi 500GB" 'Informa que o ID será "Disco001" e que a Descrição será "Disco Hitachi 500GB"
    Ent.Adicionar(_Artigos) 'Passa a classe anteriormente definida por parâmetro e adiciona
End Using

Inserir dois registos usando uma transacção

Using Ent As New TBase
    Try
        Ent.Abrir() 'Abre a conexão
        Dim _Artigos As New Artigos
        _Artigos.ID = "Disco001"
        _Artigos.Descr = "Disco Hitachi 500GB"
        Ent.Adicionar(_Artigos) 'Adiciona temporariamente o primeiro registo
        _Artigos = New Artigos
        _Artigos.ID = "Disco002" 
        _Artigos.Descr = "Disco Hitachi 550GB"
        Ent.Adicionar(_Artigos) 'Adiciona temporariamente o segundo registo
        Ent.Confirmar() 'Confirma definitivamente as alterações
    Catch ex As Exception
        Ent.Cancelar() 'Caso ocorra um erro, cancela a alteração efectuada, de modo a que nenhum dos dois registos fica-se registado na BD
        MessageBox.Show(ex.Message)
    Finally
        Ent.Fechar()
    End Try
End Using

Apagar o registo com o ID "Disco001"

Using Ent As New Base
    Ent.Apagar(Of Artigos)(Function(A) A.ID = "Disco001") 'Apaga o registo com o ID "Disco001"
End Using

Actualizar a descrição do registo com o ID "Disco001"

Using Ent As New Base
    Dim Aux As Artigos = Ent.Obter(Of Artigos)(Function(A) A.ID = "Disco001").FirstOrDefault() 'Vai buscar o registo pretendido e coloca-o numa variável
    Aux.Descr = "Disco Hitachi 1TB" 'Altera a descrição
    Ent.Guardar() 'Como a descrição foi alterada, é necessário confirmar a alteração
End Using

Resumo

Como podemos ver, o uso do Entity Framework fica ainda mais simplificado/rápido, evitando assim estar constantemente a definir novas variáveis, etc.