.NET - Apresentando o padrão de projeto Repository
O padrão de projeto Repository acrescenta uma camada de abstração no topo da camada de consultas e ajuda eliminar lógica duplicada na implementação do código de suas consultas ao modelo de entidades. |
Foi Martin Fowler que definiu o padrão Repository no seu livro - Patterns of Enterprise Application Architecture - da seguinte forma: "Intermedeia entre o domínio e as camadas de mapeamento de dados usando uma interface de coleção para acessar objetos de domínio." (numa tradução livre by Macoratti)
Neste artigo apresento os princípios do padrão de projeto Repository e como podemos implementá-lo usando LINQ e Entity Framework 4.1.
Para poder reproduzir o código usado neste artigo é recomendável que você utilize o Visual Studio 2010 ou o Visual Studio 2008.
O que é o padrão Repository ?
Um repositório é essencialmente uma coleção de objetos de domínio em memória, e, com base nisso o padrão Repository permite realizar o isolamento entre a camada de acesso a dados (DAL) de sua aplicação e sua camada de apresentação (UI) e camada de negócios (BLL).
Ao utilizar o padrão Repository você pode realizar a persistência e a separação de interesses em seu código de acesso a dados visto que ele encapsula a lógica necessária para persistir os objetos do seu domínio na sua fonte de armazenamento de dados.
Em suma, você pode usar o padrão Repository para desacoplar o modelo de domínio do código de acesso a dados.
Martin Fowler afirma: "O padrão Repository faz a mediação entre o domínio e as camadas de mapeamento de dados, agindo como uma coleção de objetos de domínio em memória.....Conceitualmente, um repositório encapsula o conjunto de objetos persistidos em um armazenamento de dados e as operações realizadas sobre eles, fornecendo uma visão mais orientada a objetos da camada de persistência.....e também da suporte ao objetivo de alcançar uma separação limpa e uma forma de dependência entre o domínio e as camadas de mapeamento de dados." (http://martinfowler.com/eaaCatalog/repository.html)
Em uma das implementações do padrão repositório podemos começar definindo uma interface que atuará como a nossa fachada de acesso aos dados conforme mostrado abaixo:
public
interface
IClienteRepository { Cliente GetByID(int clienteID); Cliente Load(int clienteID); void Save(Cliente cliente); void Delete(Cliente cliente); } |
Public
Interface
IClienteRepository Function GetByID(_clienteID As Integer) As Cliente Function Load(_clienteID As Integer) As Cliente Sub Save(_cliente As Cliente) Sub Delete(_cliente As Cliente) End Interface |
C# | VB .NET |
Definimos a interface para o repositório Cliente mas podemos ter outras interfaces para outros repositórios como Produto, Pedido, Fornecedor, etc. Abaixo temos a interface para o repositório Produto:
public
interface
IProdutoRepository { Produto GetByID(int produtoID); Produto Load(int produtoID); void Save(Produto produto); void Delete(Produto produto); } |
Public
Interface
IProdutoRepository Function GetByID(_produtoID As Integer) As Produto Function Load(_produtoID As Integer) As Produto Sub Save(_produto As Produto) Sub Delete(_produto As Produto) End Interface |
C# | VB .NET |
Podemos usar o recurso Generics para generalizar estas interfaces. Abaixo temos uma implementação da interface para qualquer repositório deste tipo:
O uso de
Generics
basicamente consiste em tipar uma coleção de modo a garantir que somente um
tipo de dado pertença a ela.
Todas as
coleções genéricas do .Net framework estão agrupadas dentro do namespace
System.Collections.Generics As principais são: |
Exemplo para repositório genérico:
public interface IRepository<T> { T GetById(int id); T Load(int id); void Save(T entity); void Delete(T entity); } |
Public Interface IRepository(Of T) Function GetById(id As Integer) As T Function Load(id As Integer) As T Sub Save(entity As T) Sub Delete(entity As T) End Interface |
C# | VB .NET |
Um Repositório para o LINQ
Para implementar o padrão Repository para o LINQ você primeiro precisa definir a interface que deve conter a declaração de todos os métodos que você deseja em seu repositório. O código abaixo mostra a interface que podemos usar para o padrão Repository com LINQ:
public interface IRepository<T> where T : class { IQueryable<T> GetAll(); void InsertOnSubmit(T entity); void DeleteOnSubmit(T entity); void SubmitChanges(); } |
Public Interface IRepository(Of T As Class) Function GetAll() As IQueryable(Of T) Sub InsertOnSubmit(entity As T) Sub DeleteOnSubmit(entity As T) Sub SubmitChanges() End Interface |
C# | VB .NET |
Após definir a interface para o seu repositório você pode usá-lo para
encapsular a forma como a recuperação, consulta e armazenamento seria realizada
em seu código de acesso a dados usando o contexto de dados (data context)
do LINQ.
Para isso basta implementar a interface IRepository em uma classe
concreta como mostra o código abaixo:
public class Repository<T> : IRepository<T> where T : class{ public DataContext Context{ get; set; } public virtual IQueryable<T> GetAll() { return Context.GetTable<T>(); } public virtual void InsertOnSubmit(T entity) { GetTable().InsertOnSubmit(entity); } public virtual void DeleteOnSubmit(T entity){ GetTable().DeleteOnSubmit(entity); } public virtual void SubmitChanges(){ Context.SubmitChanges(); } public virtual ITable GetTable(){ return Context.GetTable<T>();} } |
C# |
Public Class Repository(Of T As Class)
Implements
IRepository(Of
T) Public Property Context() As DataContext Get Return m_Context End GetSet (value As DataContext)m_Context = value End Set
End
Property Private m_Context As DataContextPublic Overridable Function GetTable() As ITableReturn Context.GetTable(Of T)()End Function
GetTable().DeleteOnSubmit(entity) End SubPublic Function GetAll() As System.Linq.IQueryable(Of T) Implements IRepository(Of T).GetAllReturn Context.GetTable(Of T)()End FunctionPublic Sub InsertOnSubmit(entity As T) Implements IRepository(Of T).InsertOnSubmitGetTable().InsertOnSubmit(entity) End SubPublic Sub SubmitChanges() Implements IRepository(Of T).SubmitChangesContext.SubmitChanges() End SubEnd Class |
VB .NET |
Um repositório para o Entity Framework
A implementação do padrão Repository usando o ADO .NET
Entity Framework segue as mesmas regras definidas anteriormente: primeiro
definimos a interface e a seguir a sua implementação.
A seguir temos um exemplo de definição da interface genérica para o Ef 4.1 :
public interface IRepository<T>{ T GetById( int id);T[] GetAll(); IQueryable<T> Query(Expression<Func<T, bool>> filter); void Save(T entity);void Delete(T entity);} |
Public Interface IRepository(Of T)Function GetById(id As Integer) As TFunction GetAll() As T()Function Query(filter As Expression(Of Func(Of T, Boolean))) As IQueryable(Of T)Sub Save(entity As T)Sub Delete(entity As T)End Interface
|
C# |
VB .NET |
Após a definição da interface podemos usá-la para implementar nossas classes do repositório. No código a seguir temos um exemplo de implementação para a classe ClienteRepository:
public
class
ClienteRepository
: IRepository<Cliente>,
IDisposable
{
private
_entidades _context; public ClienteRepository() { _context = new _entidades(); } { return _context.Clientes. Where(s => s.ClienteID == id). FirstOrDefault(); }
{ return _context.Clientes.ToArray(); }
{ return _context.Clientes.Where(filter); }
{ //código para persistir o registro do cliente para o banco de dados }
{ //Código necessário para deletar um cliente }
{ if (_context != null) { _context.Dispose(); } GC.SuppressFinalize(this); } }
|
Public Class ClienteRepositoryImplements IRepository(Of Cliente)Implements IDisposable
_context = New _entidades()End Sub
'Código necessário para deletar um cliente End Sub
Return _context.Clientes.ToArray()End Function
Return _context.Clientes.Where(Function(s) s.ClienteID = id).FirstOrDefault()End Function
Return _context.Clientes.Where(filter)End FunctionPublic Sub Save(entity As Cliente) Implements IRepository(Of Cliente).Save'código para persistir o registro do cliente para o banco de dados End Sub
If _context IsNot Nothing Then_context.Dispose() End IfGC .SuppressFinalize(Me)End SubEnd Class
|
C# |
VB .NET |
Lições aprendidas
O padrão Repository fornece assim uma maneira simples para encapsular o
código de acesso de dados em seu aplicativo permitindo também que o código seja
mais facilmente testável e que os módulos de código sejam mais facilmente
reutilizáveis; além disso ele favorece a separação de interesses entre a lógica
de acesso a dados e lógica de domínio do aplicativo.
Em essência, o padrão Repository promove a testabilidade e o uso de injeção de
dependência, reduz o acoplamento ou a coesão entre os componentes de acesso a
dados e o modelo de domínio do aplicativo e abstrai a maneira como o código de
acesso de dados é escrito em suas aplicações.
O código mostrado no artigo é apenas uma das maneiras de implementarmos o padrão Repository no LINQ e no Entity Framework 4.1. (Eu disse uma das maneiras e não a única maneira)
Obs: No artigo Entity Framework 4.1 - Conceitos - Usando o padrão Repository (inativo) vermos um exemplo completo para o Entity Framework.
"Eu sou o Alfa e o Ômega, o princípio e o fim, o primeiro e o derradeiro. Bem-aventurados aqueles que lavam as suas vestiduras no sangue do Cordeiro, para que tenha direito à árvore da vida, e possam entrar na cidade pelas portas." Apocalipse 22:13-14