.NET

15 jul, 2016

.NET – Padrão Repository e Unit of Work com EF 6 (revisitado) – Parte 02

Publicidade

Neste artigo vou recordar os conceitos relacionados ao padrão Repositório e ao 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. Hoje, vou focar no padrão Unit of Work.

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

  • Gerenciar as transações;
  • Ordenar o CRUD no banco de dados;
  • Impedir a concorrência (duplicação de atualizações);
  • Usar somente uma instância do contexto por requisição.

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: Visual Studio Community 2015

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 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 o ClienteController) e definir a instância do UnitOfWork ou injetar via construtor:

public class ClienteController : Controller
  {
       private readonly UnitOfWork uow;
       public BlogController()
        {
            uow = new UnitOfWork();
        }
        //usado para injetar via construtor
        public BlogController(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