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.