.NET - Padrão Repository e Unit of Work com EF 6 (revisitado) - II

 

 Neste artigo vou recordar os conceitos relacionados com o padrão Repositório e padrão Unit Of Work e sua implementação com o Entity Framework 6.x.

Na primeira parte deste artigo mostrei a implementação ingênua e a uma das formas corretas de implementar o padrão repositório. Neste artigo vou focar no padrão Unit of Work.

Qual a utilidade em usar o padrão Unit Of Work ?

Assim, implementar corretamente o padrão Unit Of Work, evita o problema de criar múltiplas instâncias da unidade de trabalho e permite realizar múltiplas chamadas ao método Commit() que persiste as informações em uma transação.

 

O objetivo é tornar a nossa camada de domínio baseada no Unit-of-Work e no Repositório mais simples e limpa o possível usando o mínimo de tipos e tornando-a realmente desacoplada e extensível.

 

Recursos usados :

Implementando o padrão Unit of Work : A forma correta

Tomando como base o cenário onde temos as entidades Cliente e Produto e a classe AppContexto, que representa o nosso contexto, em uma aplicação ASP .NET MVC (apenas para nos situarmos).

A classe AppContexto possui o código abaixo neste cenário:

 public class AppContexto : DbContext
 {
        public virtual DbSet<Cliente> Clientes { get; set; }
        public virtual DbSet<Produto> Produtos { get; set; }
 }

Agora nessa abordagem temos a seguinte implementação da Unit Of Work:

1- Definição da interface Unit Of Work : IUnitOFWork

  public interface IUnitOfWork
  {
        IRepositorio<Cliente> ClienteRepositorio { get; }
        IRepositorio<Produto> ProdutoRepositorio { get; }

        void Commit();
  }

Definimos um tipo Unit of Work abstrato (Interface) contendo todos os repositórios genéricos fazendo parte da unidade de trabalho com um único método Commit() usado para persistir todas as alterações feitas nos repositórios para o correspondente banco de dados.

Assim a Unit-of-Work encapsula os repositórios e é ela quem gerencia a persistência das alterações na base de dados.

Agora para cada provedor de dados precisamos criar uma única implementação concreta da  nossa abstração IUnitOfWork e IRepositorio independente do número de repositórios.

2- Implementação concreta de IUnitOfWork

  public class UnitOfWork : IUnitOfWork, IDisposable
  {
        private AppContexto _contexto = null;
        private Repositorio<Cliente> clienteRepositorio = null;
        private Repositorio<Produto> produtoRepositorio = null;
        public UnitOfWork()
        {
            _contexto = new AppContexto();
        }
        public void Commit()
        {
            _contexto.SaveChanges();
        }
        public IRepositorio<Cliente> ClienteRepositorio
        {
            get
            {
                if (clienteRepositorio == null)
                {
                    clienteRepositorio = new Repositorio<Cliente>(_contexto);
                }
                return clienteRepositorio;
            }            
        }
        public IRepositorio<Produto> ProdutoRepositorio
        {
            get
            {
                if (produtoRepositorio == null)
                {
                    produtoRepositorio = new Repositorio<Produto>(_contexto);
                }
                return produtoRepositorio;
            }
        }
       private bool disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _contexto.Dispose();
                }
            }
            this.disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 }

Esta classe implementa a IUnitOfWork onde vemos que o construtor cria uma instância do contexto que aciona o correspondente DbSet.(Veja a classe AppContexto)

Se você quiser fazer a injeção de dependência basta definir outro construtor que recebe o contexto como parâmetro.

Agora observe que nas propriedades ClienteRepositorio e ProdutoRepositorio estamos instanciando seus respectivos repositórios e passando como parâmetro uma instância do contexto garantindo assim que todos os repositórios usarão o mesmo DbContext.

Para usar a implementação em sua aplicação, no nosso caso uma aplicação ASP .NET MVC, basta criar o respectivo controlador, para o exemplo seria ClienteController , e definir a instância do UnitOfWork ou injetar via construtor:

  public class ClienteController : Controller
  {
       private readonly UnitOfWork uow;
       public ClienteController()
        {
            uow = new UnitOfWork();
        }
        //usado para injetar via construtor
        public CienteController(UnitOfWork unitOfWork)
        {
            uow = unitOfWork;
        }
        // GET: Cliente
        public ActionResult Index()
        {
             List<Cliente> clientes = uow.ClienteRepositorio.GetTudo().ToList();
             return View(clientes);
        }
        ....
    }

Pegue uma aplicação completa de Blog que mostra a implementação feita neste artigo:  MacBlog.zip (sem as referências)

(Disse Jesus) - Em verdade, em verdade vos digo que vem a hora, e agora é, em que os mortos ouvirão a voz do Filho de Deus, e os que a ouvirem viverão.
João 5:25

Referências:


José Carlos Macoratti