El patrón repositorio (repository) tiene la función de crear una capa de abstracción entre la capa de acceso de datos y la capa de lógica de negocio. Esta capa de abstracción contiene métodos de manipulación de datos que se comunican con la capa de acceso de datos para proveer los datos según las necesidades de negocio a la capa de lógica. El objetivo principal de crear esta capa es aislar la capa de acceso de datos para que los cambios no puedan afectar a la capa de lógica de negocio directamente. Este patrón nos facilitará la tarea de realizar pruebas unitarias.
Un escenario donde se puede utilizar este patrón es cuando disponemos de varias fuentes de datos. Por ejemplo una aplicación que recupere información de una base de datos SQL Server, un fichero serializado XML y un servicio web, con el patrón repositorio podremos centralizar el acceso de cada origen para no tener que dejar este menester a nuestras aplicaciones cliente.
Un ejemplo de interface sería la siguiente (podríamos añadir tantos métodos como necesitáramos, como un GetAll, AllMaching, Count, etc.):
[sourcecode language=»csharp»]
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll(Func<T, bool> predicate = null);
T Get(Func<T, bool> predicate);
void Add(T entity);
void Delete(T entity);
}
[/sourcecode]
La implementación de la interface anterior contendrá la lógica real para realizar las operaciones CRUD en la tabla Contacts:
[sourcecode language=»csharp»]
public class ContactsRepository : IRepository<Contact>
{
private SampleDbEntities entities = new SampleDbEntities();
public IEnumerable<Contact> GetAll(Func<Contact, bool> predicate = null)
{
if (predicate != null)
{
if (predicate != null)
{
return entities.Contacts.Where(predicate);
}
}
return entities.Contacts;
}
public Contact Get(Func<Contact, bool> predicate)
{
return entities.Contacts.FirstOrDefault(predicate);
}
public void Add(Contact entity)
{
entities.Contacts.AddObject(entity);
}
public void Delete(Contact entity)
{
entities.Contacts.DeleteObject(entity);
}
internal void SaveChanges()
{
entities.SaveChanges();
}
}
[/sourcecode]
Unidad de trabajo (unit of work) es el patrón que permite manejar transacciones durante la manipulación de datos utilizando el patrón repositorio. Mantiene una lista de los objetos afectados por una transacción de negocio y coordina la escritura de los cambios y la resolución de problemas de concurrencia.
Un ejemplo básico de la implementación del patrón unidad de trabajo utilizando Entity Framework se puede ver en el siguiente código:
[sourcecode language=»csharp»]
public class UnitOfWork : IDisposable
{
private SampleDbEntities entities = null;
public UnitOfWork()
{
entities = new SampleDbEntities();
}
// Add all the repository handles here
IRepository<Contact> contactRepository = null;
// Add all the repository getters here
public IRepository<Contact> ContactRepository
{
get
{
if (contactRepository == null)
{
contactRepository = new ContactsRepositoryWithUow(entities);
}
return contactRepository;
}
}
public void SaveChanges()
{
entities.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
entities.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
[/sourcecode]
El método SaveChanges() recopila todos los cambios que ha recibido la entidad en el contexto, abre una transacción en BBDD y comienza a ejecutar las modificaciones, si en este proceso se produce un error, se realiza un rollback de la transacción. Si todo va bien ejecuta un commit sobre dicha transacción.