Blog
Unit Of Work Pattern com Entity Framework
Hoje em dia fala-se muito a respeito de “Persistence Ignorance”, e, em volta deste assunto existem vários padrões. Um deles é o Unit Of Work, que é descrito pelo Martin Fowler da seguinte forma: “mantém uma lista de objetos afetados por uma transação comercial e coordena a gravação de alterações e a resolução de problemas de concorrência.”.
Este padrão é muito bem difundido, e já está presente em grande parte dos ORM`s. Podemos citar a classe DbContext no Entity Framework, a interface ITransaction no NHibernate e a classe DataContext do LINQ TO SQL e LINQConnect.
Logo, caso você adote alguma destas ferramentas de persistência de dados – o que é bem provável – não será necessário programar esse padrão do zero, apesar de que seja interessante termos nossa própria interface para o padrão Unit Of Work, encapsulando a implementação da ferramenta de persistência de dados. Mas porque isso ? Posso pensar em uma série de motivos para isso: talvez seja interessante programar alguma lógica específica para tratamento de erros, geração de log, controles de transação, etc. Mas o principal motivo é o fato de não acoplarmos a nossa aplicação a uma tecnologia especifica de persistência de dados, o que aumenta a testabilidade e ainda facilita a mudança de tecnologia de persistência de dados futuramente, caso haja necessidade.
Primeiro iremos definir a interface padrão para a nossa Unit Of Work.
public interface IUnitOfWork : IDisposable { IQueryable<TEntity> GetQueryable<TEntity>() where TEntity : class; void Attach<TEntity>(TEntity item) where TEntity : class; void SetModified<TEntity>(TEntity item) where TEntity : class; void Commit(); void CommitAndRefreshChanges(); void RollbackChanges(); }
Para a implementação concreta da interface IUnitOfWork, teremos a classe EFUnitOfWork que irá utilizar o Entity Framework para a implementação concreta da persistência de dados.
public class EFUnitOfWork : DbContext, IUnitOfWork { public IQueryable<TEntity> GetQueryable<TEntity>() where TEntity : class { return base.Set<TEntity>(); } public void Attach<TEntity>(TEntity item) where TEntity : class { base.Set<TEntity>().Attach(item); } public void SetModified<TEntity>(TEntity item) where TEntity : class { base.Entry<TEntity>(item).State = System.Data.EntityState.Modified; } public void Commit() { base.SaveChanges(); } public void CommitAndRefreshChanges() { bool saveFailed = false; do { try { base.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { saveFailed = true; ex.Entries.ToList() .ForEach(entry => { entry.OriginalValues.SetValues(entry.GetDatabaseValues()); }); } } while (saveFailed); } public void RollbackChanges() { base.ChangeTracker.Entries() .ToList() .ForEach(entry => entry.State = System.Data.EntityState.Unchanged); } }
Agora veremos um exemplo de utilização do UnitOfWork em um processo de negócio.
public sealed class ClienteDomainService { private IUnitOfWork _unitOfWork; public ClienteDomainService(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } public void AtualizarClientes(IEnumerable<Cliente> clientes) { if (clientes == null) throw new ArgumentNullException("clientes"); try { foreach (Cliente cliente in clientes) { //marca a instância da entidade como modificada _unitOfWork.SetModified<Cliente>(cliente); } //persist as alterações em banco de dados _unitOfWork.Commit(); } catch (Exception) { //rollback caso ocorra algum erro _unitOfWork.RollbackChanges(); } } }
Note que toda implementação foi feita baseada em uma interface, ou seja, tanto faz a tecnologia de persitência de dados que está por trás.
Essa flexibilidade nos permite aumentar, em muito, a flexibilidade e testabilidade do sistema.
Na hora de executar os testes unitários posso facilmente substituir a implementação concreta da Unit of Work por uma implementação fake, coisa que não seria possível caso eu utilizasse diretamente uma classe concreta.
Outro ponto positivo é que desta forma fica mais fácil mudar de tecnologia de persistência de dados caso necessário.
-
27/05/2013
NFS-e Juiz de Fora-MG: Obrigatoriedade de emissão da Nota Fiscal de Serviço Eletrônica
- 17/10/2012
- 09/10/2012
-
12/09/2012
Dynamics CRM 2011 – JavaScript Snippet – Nome dos atributos no lugar dos labels