1. Repository
He definido una interfaz genérica que implementarán todos los repositories y además alguno de ellos implementará la suya propia (y cumplir con la I de SOLID )
La interfaz genérica es esta:
public interface IRepository<T> : IDisposable where T : class
{
IQueryable<T> GetQueryable();
IEnumerable<T> GetAll();
IEnumerable<T> Find(Expression<Func<T, Boolean>> where);
T Single(Expression<Func<T, Boolean>> where);
T First(Expression<Func<T, Boolean>> where);
void Delete(T entity);
void Add(T entity);
void Attach(T entity);
void Attach(T entity, EntityStatus status);
}
Como vemos tiene definidos unos métodos genéricos para cualquier Repository. Podremos buscar todos los elementos, buscar uno en concreto o realizar búsquedas más complejas (método Find)
Recordar, que un Repository es una colección de objetos en memoria, por lo que los métodos Delete ó Add son para acciones sobre objetos en memoria; hasta que no usemos UoW para realizar la persistencia, no tendrán efecto los cambios en nuestra base de datos.
Bien, pero resulta que para los movimientos tendremos métodos específicos; por ejemplo, para conseguir un listado de movimientos paginados utilizaremos un procedimiento almacenado, igual que a la hora de recalcular el saldo de un usuario.
Podría haber optado por utilizar extension methods, pero me ha parecido más sencillo hacer una interfaz para Movimientos; queda algo así
public interface IMovimientosRepository : IRepository<Movimientos>
{
/// <summary>
/// Actualiza en base de datos el saldo de usuario.
/// </summary>
/// <param name="UserName">Nombre de usuario para actualizar saldo de movimientos</param>
void UpdateSaldo(string UserName);
/// <summary>
/// Devuelve los movimientos de un usuario, realizando la paginacion indicada
/// </summary>
/// <param name="UserName">Usuario del que buscar movimientos</param>
/// <param name="PageIndex">Numero de pagina a devolver</param>
/// <param name="PageSize">Numero de movimientos por pagina</param>
/// <returns></returns>
IEnumerable<vwMovimientos> GetMovimientos(string UserName, string Filtro, int PageIndex, int PageSize = 10);
}
Vemos que la interfaz implementa IRepository con lo que tendremos los métodos básicos de buscar y modificar, además de los propios para los movimientos.- Implementación de Repository
public class Repository<T> :IRepository<T> where T : class
{
protected readonly IObjectContextAdapter _ObjectContextAdapter;
public ObjectSet<T> _ObjectSet;
public Repository(IObjectContextAdapter context)
{
_ObjectContextAdapter = context;
_ObjectSet = context.ObjectContext.CreateObjectSet<T>();
}
public IQueryable<T> GetQueryable()
{
return _ObjectSet;
}
public IEnumerable<T> GetAll()
{
return _ObjectSet.ToList();
}
public IEnumerable<T> Find(Expression<Func<T, Boolean>> where)
{
return _ObjectSet.Where(where);
}
public T Single(Expression<Func<T, Boolean>> where)
{
return _ObjectSet.Single(where);
}
public T First(Expression<Func<T, Boolean>> where)
{
return _ObjectSet.First(where);
}
public void Delete(T entity)
{
_ObjectSet.DeleteObject(entity);
}
public void Add(T entity)
{
_ObjectSet.AddObject(entity);
}
public void Attach(T entity)
{
Attach(entity, EntityStatus.Unchanged);
}
public void Attach(T entity, EntityStatus status)
{
_ObjectSet.Attach(entity);
_ObjectContextAdapter.ObjectContext.ObjectStateManager.ChangeObjectState(entity, GetEntityState(status));
}
public void Dispose()
{
if (_ObjectContextAdapter != null)
_ObjectContextAdapter.ObjectContext.Dispose();
GC.SuppressFinalize(this);
}
private EntityState GetEntityState(EntityStatus status)
{
switch (status)
{
case EntityStatus.Added:
return EntityState.Added;
case EntityStatus.Deleted:
return EntityState.Deleted;
case EntityStatus.Detached:
return EntityState.Detached;
case EntityStatus.Modified:
return EntityState.Modified;
default:
return EntityState.Unchanged;
}
}
}
- Implementación de Movimientos Repository
public class MovimientosRepository : Repository<Movimientos>, IMovimientosRepository
{
public MovimientosRepository(IObjectContextAdapter context)
: base(context)
{
}
public void UpdateSaldo(string UserName)
{
SqlParameter parameter = new SqlParameter("@UserName", SqlDbType.NVarChar);
parameter.Value = UserName;
_ObjectContextAdapter.ObjectContext.ExecuteStoreCommand("Exec RecalculaSaldo @UserName", parameter);
}
public IEnumerable<vwMovimientos> GetMovimientos(string UserName, string Filtro, int PageIndex, int PageSize = 10)
{
var userNameParameter = new SqlParameter("@UserName", UserName);
var PageIndexParameter = new SqlParameter("@PageIndex", PageIndex);
var PageSizeParameter = new SqlParameter("@PageSize", PageSize);
var filtroParameter = new SqlParameter("@Filtro", Filtro);
var result = _ObjectContextAdapter.ObjectContext.ExecuteStoreQuery<vwMovimientos>("exec GetMovimientos @UserName, @PageIndex, @PageSize, @Filtro", userNameParameter, PageIndexParameter, PageSizeParameter, filtroParameter).ToList();
return result;
}
}
Vemos que MovimientosRepository hereda de Repository e implementa IMovimientosRepository.
Queda claro que, como veiamos en la entrada anterior, con el uso de Repository estamos evitando repetir el código que obtiene los movimientos de un usuario.
Además, todavía no hemos hablado de EF por ninguna parte ;-)
2. Unit of Work
Y ahora Unit of Work. Su interfaz
public interface IUnitOfWork : IDisposable
{
IRepository<T> Repository<T>() where T : class;
IMovimientosRepository MovimientosRepo { get; }
void SaveChanges();
}
Bien sencilla; sólo tener en cuenta que debe implementar IDisposable y que debe tener un método para realizar la persistencia (SaveChanges en este caso)Tendremos una lista de Repository genéricos y acceso al Repository de Movimientos.
Y aquí la implementación:
public class UnitOfWork : IUnitOfWork
{
private readonly IObjectContextAdapter _ObjectContextAdapter;
public Dictionary<Type, object> repositories = new Dictionary<Type, object>();
public UnitOfWork(IObjectContextAdapter objectContextAdapter)
{
_ObjectContextAdapter = objectContextAdapter;
MovimientosRepo = new MovimientosRepository(_ObjectContextAdapter);
}
public IRepository<T> Repository<T>() where T : class
{
if (repositories.Keys.Contains(typeof(T)) == true)
{
return repositories[typeof(T)] as IRepository<T>;
}
IRepository<T> repo = new Repository<T>(_ObjectContextAdapter);
repositories.Add(typeof(T), repo);
return repo;
}
public IMovimientosRepository MovimientosRepo { get; private set; }
public void SaveChanges()
{
try
{
_ObjectContextAdapter.ObjectContext.SaveChanges();
}
catch (OptimisticConcurrencyException oce)
{
//TODO:Elevar la excepcion
//throw new ConcurrencyException(oce.Message, oce.InnerException);
}
catch (DataException)
{
throw;
}
}
public void Dispose()
{
if (_ObjectContextAdapter != null)
_ObjectContextAdapter.ObjectContext.Dispose();
GC.SuppressFinalize(this);
}
}
Si nos fijamos en el constructor de las clase UnitOfWork, vemos que recibe como parámetro la interfaz IObjectContextAdapter; no recibe ningún contexto concreto, solo una interfaz; seguimos trabajando con abstracciones y no con implementaciones concretas.Bien, pues en la siguiente entrada, veremos cómo usamos todo esto desde nuestra capa de Services
No hay comentarios:
Publicar un comentario