Amc_rtty
Amc_rtty

Reputation: 3813

Customising an implementation of Repository and Unit Of Work

In my ASP.NET MVC 4 project I have carefully followed the design principles in this example of implementing Repository and Unit Of Work.

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

I am particularly interested in this remark on the article, written after showing the Generic Repository:

This generic repository will handle typical CRUD requirements. When a particular entity type has special requirements, such as more complex filtering or ordering, you can create a derived class that has additional methods for that type.

Since my app meets that particular case, I tried to do this. I made a GenericRepository just like the one in the article, and a SharedContext class (SharedContext is exactly the UnitOfWork class in the article, but this name makes more sense to me)

GenericRepository:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace BusinessLogicLayer.Repositories
{
public class GenericRepository<T> where T: class
{
    internal DbSet<T> _dbSet;
    internal DBConnection _context;

    #region constructors

    public GenericRepository(SharedContext ctx)
    {
        _context = ctx.Context;
    }

    public GenericRepository(DBConnection context)
    {
        _context = context;
        _dbSet = context.Set<T>();
    }
    #endregion

}
}

SharedContext (Unit of work) class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BusinessLogicLayer.Repositories
{
public class SharedContext : IDisposable
{
    #region repositories
    private GenericRepository<Entities.Customer> _customerRepository;
    /// <summary>
    /// access the Customer entity
    /// </summary>
    public GenericRepository<Entities.Customer> CustomerRepository
    {
        get 
        {
            if (_customerRepository == null)
                _customerRepository = new GenericRepository<Entities.Customer>(_context);

            return _customerRepository;
        }
    }
    #endregion
    
    #region context management 
    
    private Entities.DBConnection _context = new Entities.DBConnection();
    internal Entities.DBConnection Context { get { return _context; } }
    //other methods - save, delete
    #endregion
}
}

Now here is the problem: note how I exposed the Context property above - I have doubts that this is actually intended, I feel like I'm breaking the pattern by doing it. I like a lot the idea that everything is controlled through the repository in the same context and all, but I need some further methods which are not offered by the generic repository - so I made a separate CustomerRepository:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace BusinessLogicLayer.Repositories 
{
public class CustomerRepository: GenericRepository<Entities.Customer>
{
    public CustomerRepository(SharedContext ctx)
        : base(ctx)
    {  }
    
    public decimal GetCustomerBonusByFrequency()
    {
        //
        
    }
}
}

...which in my controller class, I use like this:

private SharedContext ctx = new SharedContext();

public PartialViewResult CustomerBonus()
{
   CustomerRepository cRepo = new CustomerRepository(ctx);
   var bonus = cRepo.GetCustomerBonusByFrequency();
   return PartialView(bonus);
}

So my 2 questions are:

Thx,

edit:

I needed to expose the Context property in SharedContext, because if I delete that constructor in GenericRepository (that takes a SharedContext) and the :base(ctx) in CustomerRepository class, I get this:

BusinessLogicLayer.DBModel.Customer does not contain a constructor that takes 0 arguments

Upvotes: 1

Views: 842

Answers (2)

Sanjay Patel
Sanjay Patel

Reputation: 983

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data;

namespace Demo.DAL
{
    public class Repository<TObject>

        where TObject : class
    {
        protected DB Context;
        protected DB Context = null;
        private bool shareContext = false;

        public Repository()
        {
            Context = new DB();
        }

        public Repository(DB context)
        {
            Context = context;
            shareContext = true;
        }

        protected DbSet<TObject> DbSet
        {
            get
            {
                return Context.Set<TObject>();
            }
        }

        public void Dispose()
        {
            if (shareContext && (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(Expression<Func<TObject, bool>> filter,
         out int total, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var _resetSet = filter != null ? DbSet.Where(filter).AsQueryable() : 
                DbSet.AsQueryable();
            _resetSet = skipCount == 0 ? _resetSet.Take(size) : 
                _resetSet.Skip(skipCount).Take(size);
            total = _resetSet.Count();
            return _resetSet.AsQueryable();
        }

        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)
        {
            var newEntry = DbSet.Add(TObject);
            if (!shareContext)
                Context.SaveChanges();
            return newEntry;
        }

        public virtual int Count
        {
            get
            {
                return DbSet.Count();
            }
        }

        public virtual int Delete(TObject TObject)
        {
            DbSet.Remove(TObject);
            if (!shareContext)
                return Context.SaveChanges();
            return 0;
        }

        public virtual int Update(TObject TObject)
        {
            var entry = Context.Entry(TObject);
            DbSet.Attach(TObject);
            entry.State = EntityState.Modified;
            if (!shareContext)
                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 (!shareContext)
                return Context.SaveChanges();
            return 0;
        }
    }
} 

Upvotes: 1

Brian Ball
Brian Ball

Reputation: 12596

Creating a specialized customer repository is the correct thing to do. I would suggest that your SharedContext class should be updated such that the CustomerRepository property returns an instance of the CustomerRepository class, and not of GenericRepository<Cutomer>.

This way, when you access the CustomerRepository property from the SharedContext, you will have the methods of the generic repository available as well as the specialized methods of your CustomerRepository class.

Upvotes: 1

Related Questions