4
votes

I'm new to MVC3 and have been following the awesome tutorials on the asp.net website. However, I can't quite wrap my head around how to use Unit of Work and Generic Repository patterns with Ninject. I used this tutorial as a starting point: 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

Without using interfaces, I know I can implement it like so:

Generic Repository:

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>();
    }
}

Unit of Work:

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;
}

This setup allows me to pass the same context to all repositories, and then call a single Save() function to commit the changes.

I know I can use an interface IGenericRepository<TEntity> and the concrete implementation GenericRepository<TEntity> and then bind them using Ninject:

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

But how would I go about setting up my IUnitOfWork and UnitOfWork to ensure that all my repositories share a single database context? Am I even doing it right in the first place? I've searched around but all I seem to find are tutorials that only use generic repositories without a unit of work.

1
heres my take on generic repositories, my example project should give some more details on how you implement it, also all the code for the actual generic repository is on github. blog.staticvoid.co.nz/2011/10/…Not loved
@LukeMcGregor Thanks for the link! Your blog post and code has really helped me make more sense of it all. Thanks!HTX9
Here is an alternative implementation of generic repositories.Steven
Yet a another fine example utilizing the Repository, Unit of Work, and Specification patterns: huyrua.wordpress.com/2010/07/13/…DDiVita

1 Answers

0
votes

Your base Repo:

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
}

Notice Context is an interface implementing UnitOfWork.

Your context interface:

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

}

Your IDbContext interface:

 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);
}

Then the actual context implementation:

 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);
    }



}

Then in your ninject config you simply do:

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