Dica: Assine o RSS feed ou e-mail para esta página Wiki para obter notificação automática quando ela for atualizada!




Introdução

No artigo anterior falei sobre um primeiro approach abordando o desenvolvimento de aplicações Web com ASP.NET MVC e EF para vários cenários, e este primeiro approach foi para um cenário básico, apenas separando o modelo da aplicação.

Neste post eu abordarei a criação e implementação de um padrão conhecido, o Repository Pattern, para desacoplar todo o acesso a dados da aplicação.

Para este artigo, continuarei a utilizar a mesma solução do anterior.

Repository Pattern

O Repository Pattern é um padrão de abstração da camada de acesso a dados que atua entre o domínio e a fonte de dados, persistindo e recuperando estado das entidades na fonte de dados.

Seu uso é recomendado quando há a necessidade de se ter mais de uma fonte de dados, ou risco da mudança da forma ou local de onde os dados são recuperados/persistidos.

Utilizando um repositório, sua aplicação deixa que ele controle o acesso, transações, recuperação de informações e persistencia de informações no banco, tornando isto transparente para a aplicação.

Com esta implementação, se sua aplicação necessitar trocar a fonte de dados, tudo será feito no repositório, sem afetar o resto da aplicação.

Abaixo uma ilustração de como um repositório pode funcionar

image

Podemos notar que temos um repositório para cada entidade, e cada repositório se conecta a uma fonte de dados diferente. Neste caso por exemplo, os clientes são recuperados do RBC, os pedidos do SQL Server/Oracle, o fluxo de aprovações é controlado pelo Sharepoint e as localidades (Cidades, Estados, etc..) por serem comum entre as aplicações, são compartilhadas através de um serviço.

Como comentei anteriormente, para quem irá consumir os repositórios, não importa de onde os dados vem, é transparente.

Criando um repositório

A criação de um repositório não é algo complexo. Ela consiste na criação de uma interface (Para injeção do repositório – DI) e uma classe baseada nesta interface.

Deste modo, teremos uma classe e uma interface para cada objeto do domínio, e estas serão responsáveis por saber de onde as informações referentes a entidade serão recuperadas ou persistidas.

Por questões de organização, crio sempre uma pasta chamada Repository no projeto Data da solução, e uma pasta chamada Interfaces, dentro da pasta Repository.

image

Primeiro, criaremos a interface para o repositório do Customer, adicionando um novo arquivo a pasta Interfaces chamado ICustomerRepository.

Não se esqueça de adicionar referência ao projeto AnotherMvcStore.Domain, para poder utilizar as entidades criadas.

Interface ICustomerRepository
image

A única observação nesta interface é herança da interface IDisposable, que faremos uso para destruir o repositório após seu uso.

A implementação da interface fica simples também, como utilizar o EF nos controllers do MVC.

Classe CustomerRepository.cs
image

Legal, desacoplamos o acesso a dados do cliente, mas se meu domínio tiver 30 entidades, terei que escrever este mesmo código (Substituindo Customer pela outra entidade) 30 vezes também?

Para solucionar este “problema”, criaremos um repositório genérico, que atenderá todas as entidades cuja a fonte de dados seja a padrão, no nosso caso o SQL Server, acessado via EF.

Tornando o repositório genérico

Pela internet existem várias implementações de repositórios genéricos com EF. Juntei alguns e personalizei para este post. Claro que isso não significa que esta implementação seja a melhor, é apenas uma das possíveis abordagens.

Partindo do mesmo princípio anterior, começaremos a criação da interface deste repositório generico, pensando nos métodos que sejam comuns para todas as entidades.

AnotherMvcStore.Data\Repository\Interfaces\IGenericRepository.cs
image 

Nesta interface, temos os mesmos métodos que a interface ICustomerRepository tinha, e em adicional o método Search, que recebe uma expressão para uma consulta genérica.

Na declaração da interface, temos o delimitador “where” onde restringimos o uso desta interface apenas para classes.

O desafio agora é implementar esta interface de modo que sirva para todas as entidades. Temos que pensar nos seguintes pontos:

  • Deve suportar mais que um DataContext
  • Deve ser implementada apenas
  • Deve receber vários tipos de entidades (Uma de cada vez, claro)

Pensando deste modo, optei por criar uma classe abstrata (Que só pode ser herdada), que recebe dois objetos genéricos (DataContext e Entidade).

AnotherMvcStore.Data\Repository\GenericRepository.cs
image 

Nesta primeira imagem, apenas implementei a interface inicializando como padrão o DataContext default, e posteriormente inicializei ele.

Para as demais implementações, o EF facilita bastante a vida, e basicamente substituimos o nome do DbSet para apenas Set.

O que antes ficava “_db.Customers.Add(entidade)” agora fica “_db.Set<T>().Add(entidade)”, onde T é o tipo genérico. Simples né =)

AnotherMvcStore.Data\Repository\GenericRepository.cs (Implementação dos Métodos)
image 

Melhorando o repositório criado

Até o momento escrevemos uma “porrada” de código, mas ainda não vimos a real vantagem de tudo isso. Pois bem, aqui vai:

  • Nosso repositório atual de clientes tem 17 linhas (Sem contar espaços e linhas somente com o caractere chaves).
  • Nossa interface do repositório de clientes tem 6 linhas (Sem contar espaços e linhas somente com o caractere chaves).
  • Nosso domínio (Que é apenas de exemplo) tem 4 entidades.
  • (17 + 6) * 4 = 92 linhas de código.
  • Se somarmos todas as linhas do repositório genérico e sua interface, temos um total de 83 linhas.

Resumindo, temos 83 linhas que servirão para todas as entidades que utilizarão o repositório genérico. Se aumentar nossas entidades de 4 para 10, o que ainda não é considerado um domínio simples, na implementação sem o repositório genérico, teríamos 230 linhas, e com o repositório genérico, teriamos as mesmas 83 linhas.

Após convencido (Ou não), vamos adaptar o repositório que criamos anteriormente para usufruir deste repositório genérico.

AnotherMvcStore.Data\Repository\Interfaces\ICustomerRepository.cs
image 

AnotherMvcStore.Data\Repository\CustomerRepository.cs
image 

É isto mesmo, não precisa escrever nenhuma linha de código =)

Claro que você pode extender este repositório, e criar métodos adicionais como GetCustomerByCountry(string countryCode) por exemplo, e depois implementar este método na classe. É totalmente flexível.

Lembrem-se que o que encapsulamos, foram os métodos padrões (Os mais usados), mas podem haver outros métodos e outras necessidades.

Consumindo o repositório

Incrivelmente, o modo de consumo deste repositório não irá mudar, pois já inicializamos no construtor, o DataContext padrão (Caso não informem nenhum).

A única coisa alterada foi o nome do método GetAll para ListAll e o SaveChanges para Save.

Deste modo, o controller fica assim:

AnotherMvcStore.Web\Controllers\CustomerController.cs
image 

Claro que ainda faltam alguns métodos, mas a construção do controller não é o foco ainda.

Considerações

Neste segundo approach, vimos a implementação do padrão Repository Pattern, e uma implementação genérica do mesmo, com a finalidade da redução de escrita de código.

Este padrão pode ser implementado com outros ORM, como o NHibernate por exemplo, e também pode ser implementado de outras formas, esta é apenas uma das abordagens.

Neste segundo cenário, nossa aplicação já possui o domínio e o acesso a dados desacoplado, e já serve como base para algumas aplicações que necessitem por exemplo de fontes de dados diferentes, ou até mesmo uma otimização no código e forma de acesso a dados.

Downloads

Código Fonte do Projeto

Artigos Relacionados



André Baltieri
MTAC – Microsoft Technical Audience Contributor
MSN: andrebaltieri@hotmail.com | Twitter: @andrebaltieri
Blog: http://andrebaltieri.wordpress.com

Inside .NET Users Group Leader
http://www.insidedotnet.com.br/