work unit pattern patron mvc implement framework example dependency-injection entity-framework-4.1 ninject repository-pattern unit-of-work

dependency injection - unit - MVC3 EF Unidad de trabajo+Repositorio genérico+Ninject



unit of work pattern (1)

Soy nuevo en MVC3 y he seguido los increíbles tutoriales en el sitio web asp.net. Sin embargo, no puedo entender cómo usar los patrones de Unidad de trabajo y Repositorio genérico con Ninject. Utilicé este tutorial como punto de partida: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns -in-an-asp-net-mvc-application

Sin usar interfaces, sé que puedo implementarlo así:

Repositorio genérico:

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class { internal MyContext context; internal DbSet<TEntity> dbSet; public GenericRepository(MyContext context) { this.context = context; this.dbSet = context.Set<TEntity>(); } }

Unidad de trabajo:

private MyContext context = new MyContext(); private GenericRepository<Student> studentRepository; private GenericRepository<Course> courseRepository; public GenericRepository<Student> StudentRepository { if (this.studentRepository == null) { this.studentRepository = new GenericRepository<Student>(context); } return studentRepository; } public GenericRepository<Course> CourseRepository { if (this.courseRepository == null) { this.courseRepository = new GenericRepository<Course>(context); } return courseRepository; }

Esta configuración me permite pasar el mismo contexto a todos los repositorios, y luego llamar a una sola función Guardar () para confirmar los cambios.

Sé que puedo usar una interfaz IGenericRepository<TEntity> y la implementación concreta GenericRepository<TEntity> y luego unirlos usando Ninject:

kernel.Bind(typeof(IGenericRepository<>)).To(typeof(GenericRepository<>));

¿Pero cómo voy a configurar mi IUnitOfWork y UnitOfWork para garantizar que todos mis repositorios compartan un solo contexto de base de datos? ¿Lo estoy haciendo bien desde el principio? He buscado, pero todo lo que parece encontrar son tutoriales que solo usan repositorios genéricos sin una unidad de trabajo.


Su Repo base:

public class BaseRepository<TObject> : IRepository<TObject> where TObject : class { public BaseRepository(IUnitOfWork unitOfWork) { if (unitOfWork == null) throw new ArgumentException("unitOfWork"); UnitOfWork = unitOfWork; } protected DbSet<TObject> DbSet { get { return Context.Set<TObject>(); } } public void Dispose() { if (sharedContext && (Context != null)) Context.Dispose(); } public virtual IQueryable<TObject> All() { return DbSet.AsQueryable(); } public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate) { return DbSet.Where(predicate).AsQueryable<TObject>(); } public virtual IQueryable<TObject> Filter<Key>(Expression<Func<TObject, Key>> sortingSelector, Expression<Func<TObject, bool>> filter, out int total, SortingOrders sortby = SortingOrders.Asc, int index = 0, int size = 50) { int skipCount = index * size; var _resultSet = filter != null ? DbSet.Where(filter).AsQueryable() : DbSet.AsQueryable(); total = _resultSet.Count(); _resultSet = sortby == SortingOrders.Asc ? _resultSet.OrderBy(sortingSelector).AsQueryable() : _resultSet.OrderByDescending(sortingSelector).AsQueryable(); _resultSet = skipCount == 0 ? _resultSet.Take(size) : _resultSet.Skip(skipCount).Take(size); return _resultSet; } public bool Contains(Expression<Func<TObject, bool>> predicate) { return DbSet.Count(predicate) > 0; } public virtual TObject Find(params object[] keys) { return DbSet.Find(keys); } public virtual TObject Find(Expression<Func<TObject, bool>> predicate) { return DbSet.FirstOrDefault(predicate); } public virtual TObject Create(TObject TObject, bool SaveChanges = true) { var newEntry = DbSet.Add(TObject); if (!sharedContext && SaveChanges) Context.SaveChanges(); return newEntry; } public virtual int Count { get { return DbSet.Count(); } } public virtual int Delete(TObject TObject) { DbSet.Remove(TObject); if (!sharedContext) return Context.SaveChanges(); return 0; } public virtual int Update(TObject TObject, bool SaveChanges = true) { var entry = Context.Entry(TObject); DbSet.Attach(TObject); entry.State = EntityState.Modified; if (!sharedContext && SaveChanges) return Context.SaveChanges(); return 0; } public virtual int Delete(Expression<Func<TObject, bool>> predicate) { var objects = Filter(predicate); foreach (var obj in objects) DbSet.Remove(obj); if (!sharedContext) return Context.SaveChanges(); return 0; } /// <summary> /// Sets the state of an entity. /// </summary> /// <param name="entity">object to set state.</param> /// <param name="entityState"><see cref="EntityState"/></param> protected virtual void SetEntityState(object entity, EntityState entityState) { Context.Entry(entity).State = entityState; } /// <summary> /// /// </summary> /// <param name="entity"></param> protected virtual void Attach(object entity) { if (Context.Entry(entity).State == EntityState.Detached) Context.Entry(entity).State = EntityState.Modified; } protected virtual void Detach(object entity) { Context.Entry(entity).State = EntityState.Detached; } public void SubmitChanges() { UnitOfWork.SaveChanges(); } #region Properties private bool sharedContext { get; set; } /// <summary> /// Unit of work controlling this repository. /// </summary> protected IUnitOfWork UnitOfWork { get; set; } /// <summary> /// Provides access to the ef context we are working with /// </summary> internal IMyContext Context { get { return (IMyContext)UnitOfWork; } } #endregion }

El contexto de notificación es una interfaz que implementa UnitOfWork.

Su interfaz de contexto:

public interface IMyContext : IDbContext { DbSet<Sometype> SomeProperty { get; set; } ... }

Su interfaz IDbContext:

public interface IDbContext { DbChangeTracker ChangeTracker { get; } DbContextConfiguration Configuration { get; } Database Database { get; } void Dispose(); void Dispose(bool disposing); DbEntityEntry Entry(object entity); DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class; bool Equals(object obj); int GetHashCode(); Type GetType(); IEnumerable<DbEntityValidationResult> GetValidationErrors(); void OnModelCreating(DbModelBuilder modelBuilder); int SaveChanges(); DbSet<TEntity> Set<TEntity>() where TEntity : class; DbSet Set(Type entityType); bool ShouldValidateEntity(DbEntityEntry entityEntry); string ToString(); DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items); }

Entonces la implementación del contexto real:

public class MyContext : DbContext, IUnitOfWork, IMyContext { //public MyContext() //{ // Database.SetInitializer<ReconContext>(null); //} public ReconContext() : base("Name=ReconContext") { ((IObjectContextAdapter)this).ObjectContext.ContextOptions.ProxyCreationEnabled = false; } public DbSet<SomeType> SomeProperty { get; set; } .... public new void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new SomePropertyMap()); ..... base.OnModelCreating(modelBuilder); } int IUnitOfWork.SaveChanges() { return base.SaveChanges(); } void IDisposable.Dispose() { base.Dispose(); } public new void Dispose(bool disposing) { base.Dispose(disposing); } public new bool ShouldValidateEntity(DbEntityEntry entityEntry) { return base.ShouldValidateEntity(entityEntry); } public new DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) { return base.ValidateEntity(entityEntry, items); } }

Luego, en su configuración de ninject simplemente lo hace:

kernel.Bind<IUnitOfWork<MyContext>>().To<MyContext>().InRequestScope();