.NET - O padrão Unit of Work (EF 4.1)
Neste artigo abordarei os conceitos relacionados com o padrão de projeto Unit Of Work e sua implementação com o Entity Framework 4.1.
De acordo com Martin Fowler, que definiu o padrão no seu livro - Patterns of Enterprise Application Architecture , o padrão Unit Of Work (unidade de trabalho) : "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." (numa tradução livre by Macoratti)
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 Unit of Work ?
O padrão Unit of Work esta presente em quase todas as ferramentas OR/M atuais (digo quase pois não conheço todas) e geralmente você não terá que criar a sua implementação personalizada a menos que decida realmente fazer isso por uma questão de força maior.
Dessa forma a interface ITransaction no NHibernate, a classe DataContext no LINQ to SQL e a classe ObjectContext no Entity Framwork são exemplos de implementações do padrão Unit of Work. (Até o famigerado DataSet pode ser usado como uma Unit of Work.)
Então o padrão Unit of Work pode ser visto como um contexto, sessão ou objeto que acompanha as alterações das entidades de negócio durante uma transação sendo também responsável pelo gerenciamento dos problemas de concorrência que podem ocorrer oriundos dessa transação.
Como usar o padrão Unit of Work ?
Uma das melhores maneiras de usar o padrão Unit of Work é permitir que classes e serviços diferentes façam parte em uma única transação lógica sem se conhecerem mutuamente.
Em uma das implementações do padrão repositório podemos começar definindo uma interface (da mesma forma que no padrão repository) conforme mostrado abaixo:
public
interface
IUnitOfWork<T>
{ void RegisterNew(T entity); void RegisterDirty(T entity); void RegisterClean(T entity); void RegisterDeleted(T entity); void Commit(); } |
Public
Interface
IUnitOfWork(Of
T)
Sub RegisterNew(entity As T) Sub RegisterDirty(entity As T) Sub RegisterClean(entity As T) Sub RegisterDeleted(entity As T) Sub Commit() End Interface |
C# |
VB .NET |
Observe que no exemplo estamos registrando as
alterações das entidades dentro da unidade de trabalho (unit of work)
e
no final de cada transação efetivamos a gravação usando o método Commit.
Como eu já mencionei em quase todas as ferramentas OR/M disponíveis
atualmente temos esta funcionalidade definida dentro da sessão ou contexto dos
objetos/entidades.
È a unidade de trabalho que gerencia os detalhes de cada alteração que
realizamos sobre uma entidade. No exemplo foram definidos quatro métodos para
tratamento dos registros além do método Commit mas você pode utilizar uma
interface mais simples conforme vemos abaixo:
public
interface
IUnitOfWork<T>
{ void RegisterNew(T entity); void Commit(); } |
Public
Interface
IUnitOfWork(Of
T)
Sub RegisterNew(entity As T) Sub Commit() End Interface |
C# | VB .NET |
Você também pode optar por implementar a funcionalidade para desfazer as alterações feitas antes de efetivá-las definindo um método Rollback .(Ao usar Entity Framework, a abordagem recomendada para desfazer é a de descartar o seu contexto com as mudanças que você está interessado em desfazer.)
Como o padrão Repository e o padrão Unit Of Work caminham lado a lado eu vou usar o exemplo do repositório definido no artigo: .NET - Apresentando o padrão de projeto Repository
Abaixo vemos o código da implementação do padrão Repository a classe ClienteRepository que eu simplifiquei mais ainda removendo os métodos Save, Delete e Query para tornar mais claro o exemplo:
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(); }
{ if (_context != null) { _context.Dispose(); } GC.SuppressFinalize(this); } } |
Public Class ClienteRepositoryImplements IRepository(Of Cliente)Implements IDisposable
_context = New _entidades()End Sub
Return _context.Clientes.ToArray()End Function
Return _context.Clientes.Where(Function(s) s.ClienteID = id).FirstOrDefault()End Function
If _context IsNot Nothing Then_context.Dispose() End IfGC .SuppressFinalize(Me)End SubEnd Class
|
Vamos transformar a classe ClienteRepository em uma unidade de trabalho para gerenciar clientes incluindo na classe o seguinte trecho de código:
public
void
RegisterNew(Cliente
entity)
{ _context.AddToClientes(entity); }
{ _context.SaveChanges(); } |
Public
Sub
RegisterNew(entity
As
Cliente)
_context.AddToClientes(entity)
End
Sub _context.SaveChanges() End Sub |
C# |
VB .NET |
O código completo da implementação ficaria assim:
public
class
ClienteRepository
: IRepository<Cliente>,IUnitOfWork<Cliente>,
IDisposable
{ private _entidades _context; public ClienteRepository() { _context = new _entidades(); } public Cliente GetById(int id) { return _context.Clientes. Where(s => s.ClienteID == id). FirstOrDefault(); } public Cliente[] GetAll() { return _context.Clientes.ToArray(); } public void RegisterNew(Cliente entity) { _context.AddToClientes(entity); } public void Commit() { _context.SaveChanges(); } public void Dispose() { if (_context != null) { _context.Dispose(); } GC.SuppressFinalize(this); } } |
Public
Class
ClienteRepository
Implements
IRepository(Of
Cliente)
Implements IUnitOfWork(Of Cliente)
Implements
IDisposable
_context = New _entidades()
End
Sub If _context IsNot Nothing Then _context.Dispose() End If GC.SuppressFinalize(Me) End Sub Public Function GetById(id As Integer) As Cliente Implements IRepository(Of Cliente).GetById Return _context.Clientes.Where(Function(s) s.ClienteID = id).FirstOrDefault()
End
Function Return _context.Clientes.ToArray()
End
Function _context.AddToClientes(entity)
End
Sub _context.SaveChanges() End Sub End Class |
C# |
VB .NET |
Lições aprendidas
Seguindo a definição e orientações de Martin Fowler definimos uma interface bem simples para nossa unidade de trabalho e usando o Entity Framework fizemos nossa própria implementação gerenciando apenas o registro de novas entidades a sua persistência.
Existem implementações bem mais complexas e mais robustas do que foi mostrado aqui. O nosso exemplo serviu apenas para introduzir o assunto.
Aguarde em breve novos artigos sobre o padrão Unit of Work.
"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
Referências: