IObjectContext.cs
namespace Infrastructure.Contracts
{
using System;
/// <summary>
/// Interfaz que encapsula firmas de métodos del contexto.
/// </summary>
public interface IObjectContext:IDisposable
{
/// <summary>
/// Método para guardar cambios en el contexto.
/// </summary>
void SaveChanges();
/// <summary>
/// Método para revertir una transacción.
/// </summary>
void RollBack();
}
}
IObjectSetFactory.cs
namespace Infrastructure.Contracts
{
using System;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
/// <summary>
/// Interfaz que encapsula las firmas de los métodos de la fabrica encargada de gestionar la instancia
/// de las entidades y su estado.
/// </summary>
public interface IObjectSetFactory: IDisposable
{
/// <summary>
/// Método para crear objetos del contexto.
/// </summary>
/// <typeparam name="T">Tipo de entidad.</typeparam>
/// <returns>Fabrica.</returns>
IObjectSet<T> CreateObjectSet<T>() where T : class;
/// <summary>
/// Método para cambiar el estado de una entidad.
/// </summary>
/// <param name="entity">Entidad.</param>
/// <param name="state">Estado.</param>
void ChangeObjectState(object entity, EntityState state);
}
}
IRepository.cs
namespace Infrastructure.Contracts
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
/// <summary>
/// Interfaz que encapsula las firmas de los métodos del repositorio genérico.
/// </summary>
/// <typeparam name="T">Tipo de clase de la que se desea crear el repositorio.</typeparam>
public interface IRepository<T> where T : class
{
/// <summary>
/// Método para agregar una entidad al contexto.
/// </summary>
/// <param name="entity">Entidad.</param>
void Insert(T entity);
/// <summary>
/// Método para eliminar una entidad del contexto.
/// </summary>
/// <param name="entity">Entidad.</param>
void Delete(T entity);
/// <summary>
/// Método para actualizar una entidad del contexto.
/// </summary>
/// <param name="entity">Entidad.</param>
void Update(T entity);
/// <summary>
/// Método para obtener una entidad que satisfaga una expresión lambda.
/// </summary>
/// <param name="where">Expresión lambda.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en la entidad.</param>
/// <returns>Entidad.</returns>
T Single(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties);
/// <summary>
/// Método para obtener la primera entidad que satisface una expresión lambda.
/// </summary>
/// <param name="where">Expresión lambda.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en la entidad.</param>
/// <returns>Entidad.</returns>
T First(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties);
/// <summary>
/// Método para obtener todos los registros de una entidad.
/// </summary>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Conjunto de entidades.</returns>
IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includeProperties);
/// <summary>
/// Método para obtener todos aquellos registros de una entidad que satisfacen una expresión lambda.
/// </summary>
/// <param name="where">Expresión lambda.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Entidades</returns>
IEnumerable<T> Find(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties);
/// <summary>
/// Método para obtener todos los registros de una entidad de forma paginada.
/// </summary>
/// <param name="pageNumber">Número de página.</param>
/// <param name="pageSize">Tamaño de página.</param>
/// <param name="totalPages">Total de páginas.</param>
/// <param name="totalElements">Total de elementos.</param>
/// <param name="orderBy">Criterio de ordenamiento y propiedad por la que se va a ordenar.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Conjunto de entidades paginadas.</returns>
IEnumerable<T> GetAllWithPagination(int pageNumber, int pageSize, out int totalPages, out int totalElements, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includeProperties);
/// <summary>
/// Método para obtener de forma paginada todos aquellos registros de una entidad que satisfacen una expresión lambda.
/// </summary>
/// <param name="pageNumber">Número de página.</param>
/// <param name="pageSize">Tamaño de página.</param>
/// <param name="totalPages">Total de páginas.</param>
/// <param name="totalElements">Total de elementos.</param>
/// <param name="where">Expresión lambda.</param>
/// <param name="orderBy">Criterio de ordenamiento y propiedad por la que se va a ordenar.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Entidades.</returns>
IEnumerable<T> FindWithPagination(int pageNumber, int pageSize, out int totalPages, out int totalElements, Expression<Func<T, bool>> where, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includeProperties);
}
}
IUnitOfWork.cs
namespace Infrastructure.Contracts
{
/// <summary>
/// Interfaz que encapsula las firmas de los métodos de la unidad de trabajo.
/// </summary>
public interface IUnitOfWork
{
/// <summary>
/// Método para comprometer una transacción.
/// </summary>
void Commit();
/// <summary>
/// Método para revertir una transacción.
/// </summary>
void RollBack();
}
}
DbContextAdapter.cs
namespace Infrastructure.Implementation
{
using Infrastructure.Contracts;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Linq;
/// <summary>
/// Clase que encapsula un adaptador para el contexto de base de datos.
/// </summary>
public class DbContextAdapter : IObjectSetFactory, IObjectContext
{
// Variable que encapsula la instancia del contexto.
private readonly ObjectContext Context;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">Contexto.</param>
public DbContextAdapter(DbContext context)
{
Context = context.GetObjectContext();
}
/// <summary>
/// Método para guardar cambios en el contexto.
/// </summary>
public void SaveChanges()
{
Context.SaveChanges();
}
/// <summary>
/// Método para revertir una transacción.
/// </summary>
public void RollBack()
{
IEnumerable<object> _objects = from e in Context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Deleted)
select e.Entity;
Context.Refresh(RefreshMode.StoreWins, _objects);
IEnumerable<object> AddedCollection = from e in Context.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
select e.Entity;
foreach (object addedEntity in AddedCollection)
Context.Detach(addedEntity);
}
/// <summary>
/// Método para crear objetos del contexto.
/// </summary>
/// <typeparam name="T">Tipo de entidad.</typeparam>
/// <returns>Fabrica.</returns>
public IObjectSet<T> CreateObjectSet<T>() where T : class
{
return Context.CreateObjectSet<T>();
}
/// <summary>
/// Método para cambiar el estado de una entidad.
/// </summary>
/// <param name="entity">Entidad.</param>
/// <param name="state">Estado.</param>
public void ChangeObjectState(object entity, EntityState state)
{
Context.ObjectStateManager.ChangeObjectState(entity, state);
}
/// <summary>
/// Método para liberar el contexto.
/// </summary>
public void Dispose()
{
Context.Dispose();
}
}
}
DbContextExtensions.cs
namespace Infrastructure.Implementation
{
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
/// <summary>
/// Clase que contiene métodos de extensión del contexto.
/// </summary>
public static class DbContextExtensions
{
/// <summary>
/// Método para obtener la instancia del contexto.
/// </summary>
/// <param name="dbContext">Contexto.</param>
/// <returns>Instancia del contexto.</returns>
public static ObjectContext GetObjectContext(this DbContext dbContext)
{
return ((IObjectContextAdapter)dbContext).ObjectContext;
}
}
}
Repository.cs
namespace Infrastructure.Implementation
{
using Infrastructure.Contracts;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Linq;
using System.Linq.Expressions;
/// <summary>
/// Clase que encapsula la implementación de los métodos del repositorio genérico.
/// </summary>
public class Repository<T> : IRepository<T> where T : class
{
// Interface IObjectSet.
private readonly IObjectSet<T> ObjectSet;
// Interface IObjectSetFactory.
private readonly IObjectSetFactory ObjectSetFactory;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="objectSetFactory">Fabrica de entidades.</param>
public Repository(IObjectSetFactory objectSetFactory)
{
ObjectSet = objectSetFactory.CreateObjectSet<T>();
ObjectSetFactory = objectSetFactory;
}
/// <summary>
/// Método que proporciona una instancia para evaluar consultas.
/// </summary>
/// <returns>Instancia con funcionalidad para evaluar consultas.</returns>
public IQueryable<T> AsQueryable()
{
return ObjectSet;
}
/// <summary>
/// Método para agregar una entidad al contexto.
/// </summary>
/// <param name="entity">Entidad.</param>
public void Insert(T entity)
{
ObjectSet.AddObject(entity);
}
/// <summary>
/// Método para eliminar una entidad del contexto.
/// </summary>
/// <param name="entity">Entidad.</param>
public void Delete(T entity)
{
ObjectSet.DeleteObject(entity);
}
/// <summary>
/// Método para actualizar una entidad del contexto.
/// </summary>
/// <param name="entity">Entidad.</param>
public void Update(T entity)
{
ObjectSet.Attach(entity);
ObjectSetFactory.ChangeObjectState(entity, EntityState.Modified);
}
/// <summary>
/// Método para obtener una entidad que satisfaga una expresión lambda.
/// </summary>
/// <param name="where">Expresión lambda.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en la entidad.</param>
/// <returns>Entidad.</returns>
public T Single(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> _query = AsQueryable();
_query = PerformInclusions(includeProperties, _query);
return _query.Single(where);
}
/// <summary>
/// Método para obtener la primera entidad que satisface una expresión lambda.
/// </summary>
/// <param name="where">Expresión lambda.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en la entidad.</param>
/// <returns>Entidad.</returns>
public T First(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> _query = AsQueryable();
_query = PerformInclusions(includeProperties, _query);
return _query.FirstOrDefault(where);
}
/// <summary>
/// Método para obtener todos los registros de una entidad.
/// </summary>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Conjunto de entidades.</returns>
public IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> _query = AsQueryable();
return PerformInclusions(includeProperties, _query);
}
/// <summary>
/// Método para obtener todos aquellos registros de una entidad que satisfacen una expresión lambda.
/// </summary>
/// <param name="where">Expresión lambda.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Entidades.</returns>
public IEnumerable<T> Find(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> _query = AsQueryable();
_query = PerformInclusions(includeProperties, _query);
return _query.Where(where);
}
/// <summary>
/// Método para incluir en una consulta una o mas propiedades de navegación.
/// </summary>
/// <param name="includeProperties">Propiedades de navegación.</param>
/// <param name="query">Consulta.</param>
/// <returns>Consulta con las propiedades incluidas.</returns>
private static IQueryable<T> PerformInclusions(IEnumerable<Expression<Func<T, object>>> includeProperties, IQueryable<T> query)
{
return includeProperties.Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
}
/// <summary>
/// Método para obtener todos los registros de una entidad de forma paginada.
/// </summary>
/// <param name="pageNumber">Número de página.</param>
/// <param name="pageSize">Tamaño de página.</param>
/// <param name="totalPages">Total de páginas.</param>
/// <param name="totalElements">Total de elementos.</param>
/// <param name="orderBy">Criterio de ordenamiento y propiedad por la que se va a ordenar.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Conjunto de entidades paginadas.</returns>
public IEnumerable<T> GetAllWithPagination(int pageNumber, int pageSize, out int totalPages, out int totalElements, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includeProperties)
{
if (pageNumber < 1 || pageSize < 1)
{
totalElements = 0;
totalPages = 0;
throw new ArgumentOutOfRangeException(null, "Los valores para número y tamaño de página deben ser mayores a 0.");
}
else
{
IQueryable<T> _query = AsQueryable();
_query = PerformInclusions(includeProperties, _query);
if (orderBy != null)
_query = orderBy(_query);
totalElements = _query.Count();
totalPages = CalculateTotalPages(_query.Count(), pageSize);
return _query.Skip(GetSkip(pageNumber, pageSize)).Take(pageSize);
}
}
/// <summary>
/// Método para obtener de forma paginada todos aquellos registros de una entidad que satisfacen una expresión lambda.
/// </summary>
/// <param name="pageNumber">Número de página.</param>
/// <param name="pageSize">Tamaño de página.</param>
/// <param name="totalPages">Total de páginas.</param>
/// <param name="totalElements">Total de elementos.</param>
/// <param name="where">Expresión lambda.</param>
/// <param name="orderBy">Criterio de ordenamiento y propiedad por la que se va a ordenar.</param>
/// <param name="includeProperties">Propiedades de navegación que se desea incluir en cada una de las entidades.</param>
/// <returns>Entidades.</returns>
public IEnumerable<T> FindWithPagination(int pageNumber, int pageSize, out int totalPages, out int totalElements, Expression<Func<T, bool>> where, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includeProperties)
{
if (pageNumber < 1 || pageSize < 1)
{
totalPages = 0;
totalElements = 0;
throw new ArgumentOutOfRangeException(null, "Los valores para número y tamaño de página deben ser mayores a 0.");
}
else
{
IQueryable<T> _query = AsQueryable();
_query = (PerformInclusions(includeProperties, _query)).Where(where);
if (orderBy != null)
_query = orderBy(_query);
totalElements = _query.Count();
totalPages = CalculateTotalPages(_query.Count(), pageSize);
return _query.Skip(GetSkip(pageNumber, pageSize)).Take(pageSize);
}
}
/// <summary>
/// Método para calcular el total de páginas.
/// </summary>
/// <param name="totalItems">Total de elementos.</param>
/// <param name="pageSize">Tamaño de la página.</param>
/// <returns>Total de páginas.</returns>
private int CalculateTotalPages(int totalItems, int pageSize)
{
int _totalPages = -1;
if (totalItems == 0)
_totalPages = 0;
else if ((totalItems % pageSize) == 0)
_totalPages = totalItems / pageSize;
else
_totalPages = (totalItems / pageSize) + 1;
return _totalPages;
}
/// <summary>
/// Método para saber cuantos elementos se van a omitir.
/// </summary>
/// <param name="pageNumber">Número de página.</param>
/// <param name="pageSize">Tamaño de la página.</param>
/// <returns>Número de elementos que se deberan de omitir.</returns>
private int GetSkip(int pageNumber, int pageSize)
{
return (pageSize * (pageNumber - 1));
}
}
}
UnitOfWork.cs
namespace Infrastructure.Implementation
{
using Infrastructure.Contracts;
using System;
/// <summary>
/// Clase que encapsula la implementación de los métodos de la unidad de trabajo.
/// </summary>
public class UnitOfWork : IUnitOfWork, IDisposable
{
// Interface IObjectContext.
private readonly IObjectContext ObjectContext;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="objectContext">Instancia del contexto.</param>
public UnitOfWork(IObjectContext objectContext)
{
ObjectContext = objectContext;
}
/// <summary>
/// Método para comprometer una transacción.
/// </summary>
public void Commit()
{
ObjectContext.SaveChanges();
}
/// <summary>
/// Método para revertir una transacción.
/// </summary>
public void RollBack()
{
ObjectContext.RollBack();
}
/// <summary>
/// Método para liberar la instancia del contexto.
/// </summary>
public void Dispose()
{
if (ObjectContext != null)
ObjectContext.Dispose();
GC.SuppressFinalize(this);
}
}
}