5
\$\begingroup\$

I put together a sample of how I am using the Unit of Work & Repository pattern based on my understanding. Can anyone please let me know if I am implementing this the correct way? If I am not, how can I improve it?

I have an EF model with two entities: Topic and Subtopic. The EF model is called CommonGood.

Unit of Work:

    /// <summary>
/// Implementation of a UnitOfWork class
/// </summary>
public static class UnitOfWork
{
    /// <summary>
    /// Gets the default context
    /// </summary>
    /// <returns>A new instance of the default context</returns>
    public static CommonGoodEntities GetContext()
    {
        return new CommonGoodEntities();
    }
}

IGenericRepository:

public interface IRepository<T>
{
    /// <summary>
    /// Gets all entities
    /// </summary>        
    /// <returns>All entities</returns>
    IEnumerable<T> GetAll();

    /// <summary>
    /// Gets all entities matching the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>All entities matching the predicate</returns>
    IEnumerable<T> GetAll(Expression<Func<T, bool>> predicate);

    /// <summary>
    /// Set based on where condition
    /// </summary>
    /// <param name="predicate">The predicate</param>
    /// <returns>The records matching the given condition</returns>
    IQueryable<T> Where(Expression<Func<T, bool>> predicate);

    /// <summary>
    /// Finds an entity matching the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>An entity matching the predicate</returns>
    IEnumerable<T> Find(Expression<Func<T, bool>> predicate);

    /// <summary>
    /// Determines if there are any entities matching the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>True if a match was found</returns>
    bool Any(Expression<Func<T, bool>> predicate);

    /// <summary>
    /// Returns the first entity that matches the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>An entity matching the predicate</returns>
    T First(Expression<Func<T, bool>> predicate);

    /// <summary>
    /// Returns the first entity that matches the predicate else null
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>An entity matching the predicate else null</returns>
    T FirstOrDefault(Expression<Func<T, bool>> predicate);

    /// <summary>
    /// Adds a given entity to the context
    /// </summary>
    /// <param name="entity">The entity to add to the context</param>
    void Add(T entity);

    /// <summary>
    /// Deletes a given entity from the context
    /// </summary>
    /// <param name="entity">The entity to delete</param>
    void Delete(T entity);

    /// <summary>
    /// Attaches a given entity to the context
    /// </summary>
    /// <param name="entity">The entity to attach</param>
    void Attach(T entity);
}

GenericRepository:

public class GenericRepository<T> : IRepository<T> where T : class
{
    /// <summary>
    /// The database context for the repository
    /// </summary>
    private DbContext _context;

    /// <summary>
    /// The data set of the repository
    /// </summary>
    private IDbSet<T> _dbSet;

    /// <summary>
    /// Initializes a new instance of the <see cref="GenericRepository{T}" /> class.        
    /// </summary>
    /// <param name="context">The context for the repository</param>        
    public GenericRepository(DbContext context)
    {
        this._context = context;
        this._dbSet = this._context.Set<T>();
    }

    /// <summary>
    /// Gets all entities
    /// </summary>        
    /// <returns>All entities</returns>
    public IEnumerable<T> GetAll()
    {
        return this._dbSet;
    }

    /// <summary>
    /// Gets all entities matching the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>All entities matching the predicate</returns>
    public IEnumerable<T> GetAll(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.Where(predicate);
    }

    /// <summary>
    /// Set based on where condition
    /// </summary>
    /// <param name="predicate">The predicate</param>
    /// <returns>The records matching the given condition</returns>
    public IQueryable<T> Where(Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.Where(predicate);
    }

    /// <summary>
    /// Finds an entity matching the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>An entity matching the predicate</returns>
    public IEnumerable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.Where(predicate);
    }

    /// <summary>
    /// Determines if there are any entities matching the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>True if a match was found</returns>
    public bool Any(Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.Any(predicate);
    }

    /// <summary>
    /// Returns the first entity that matches the predicate
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>An entity matching the predicate</returns>
    public T First(Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.First(predicate);
    }

    /// <summary>
    /// Returns the first entity that matches the predicate else null
    /// </summary>
    /// <param name="predicate">The filter clause</param>
    /// <returns>An entity matching the predicate else null</returns>
    public T FirstOrDefault(Expression<Func<T, bool>> predicate)
    {
        return this._dbSet.FirstOrDefault(predicate);
    }

    /// <summary>
    /// Adds a given entity to the context
    /// </summary>
    /// <param name="entity">The entity to add to the context</param>
    public void Add(T entity)
    {
        this._dbSet.Add(entity);
    }

    /// <summary>
    /// Deletes a given entity from the context
    /// </summary>
    /// <param name="entity">The entity to delete</param>
    public void Delete(T entity)
    {
        this._dbSet.Remove(entity);
    }

    /// <summary>
    /// Attaches a given entity to the context
    /// </summary>
    /// <param name="entity">The entity to attach</param>
    public void Attach(T entity)
    {
        this._dbSet.Attach(entity);
    }
}

Controller:

public class HomeController : Controller
{
    /// <summary>
    /// The context used for the controller
    /// </summary>
    private DbContext _context;

    /// <summary>
    /// Initializes a new instance of the <see cref="HomeController"/> class.
    /// </summary>        
    public HomeController()
    {
        this._context = UnitOfWork.GetContext();
    }

    public JsonResult GetTopics()
    {
        var topics = new GenericRepository<Topic>(this._context).GetAll().ToList();            
        return this.Json(topics, JsonRequestBehavior.AllowGet);
    }

    /// <summary>
    /// Disposes of the context if the currently disposing
    /// </summary>
    /// <param name="disposing">A value indicating whether or not the application is disposing</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            this._context.Dispose();
        }

        base.Dispose(disposing);
    }
}

Essentially I want to make sure I am accessing data in the proper way and making sure I am not overlooking anything.

\$\endgroup\$
  • 1
    \$\begingroup\$ While accepting answers is an excellent idea, you should give it some more time, perhaps 2-3 days, to let the votes roll in and keep your question appealing to other answerers. Prematurely accepted answers tend to get low views and low votes, and that's bad because sometimes they're gems! \$\endgroup\$ – Mathieu Guindon Oct 31 '13 at 19:50
6
\$\begingroup\$

You're getting there. Here's a pattern that I've used that works really well. I don't like generic "find" and "save" methods because they tend to be really complicated in order to work with everything and I find you end up pulling your hair out when you want to do something special.

Here's the base repository class. We want concrete implementations so we throw abstract on there

public abstract class BaseRepository
{

    protected UnitOfWork _uow;

    public BaseRepository(UnitOfWork unitOfWork) {
        _uow = unitOfWork;
    }

    protected MyEntities DB {
        get { return _uow.DB; }
    }

}

Heres the Unit of work implementation. An option here is having your UnitOfWork implement IDisposable

public class UnitOfWork
{
    private MyEntities _db;

    public MyEntities DB {
        get { return _db; }
    }

    public UnitOfWork() {
        _db = new MyEntities ();     
    }

    public void SaveChanges() {
        _db.SaveChanges();
    }

}

The implementation of a repository. I generally make a repository separating different entities, but there are several logical ways of separating them. In this repository you write a method that corresponds to a requirement for something like "GetAllDocuments" or "GetPerson". You then call the entities context and work on it directly. This provides the separation of the Data Access Layer with the Business Layer.

public class MyRepository : BaseRepository
{
    public MyRepository (UnitOfWork unitOfWork)
        : base(unitOfWork)
    {
    }

    public MyObject GetObject(int objectID)
    {
        return DB.Objects.FirstOrDefault(n => n.ObjectID == ObjectID);
    }
}

A base controller holds the unit of work

public class BaseController : Controller
{
    private UnitOfWork             _uow;

    protected UnitOfWork UOW {
        get {
            if (_uow == null) {
                _uow = new UnitOfWork();
            }
            return _uow;
        }
    }
}

Instantiate the repository you want to work out of in the constructor of the controller, passing in the unit of work variable that comes from the base controller.

public class HomeController : BaseController
{
    MyRepository _myrepo;

    public HomeController()
    {
         _myrepo = new MyRepository(UOW);
    }
}

Now in any of your methods, you have access to your database through business requirements without touching EF implementation

public ActionResult Index()
{
    _myRepo.GetObject(myInt);

    return View();
}

You can use multiple repos in the same controller by simply instantiating another repo. Since the unit of work is the same they are the same context, so you can work with both at the same time.

MyRepository _myrepo;
MyOtherRepository _myOtherRepo;

public HomeController()
{
     _myrepo = new MyRepository(UOW);
     _myOtherRepo = new MyOtherRepository(UOW);
}

Because of the way controllers work, a cool consequence of all this is that your contexts are "short lived". Once your GET or POST method returns, the context is destroyed.

\$\endgroup\$
  • \$\begingroup\$ A nice addition to this would be Ninject too, good answer though \$\endgroup\$ – Daniel Dawes Nov 1 '13 at 14:15
  • 1
    \$\begingroup\$ Any chance you could expand on this answer by showing the relationship between the projects/classes where the different code resides and how it is accessed. To better understand how projects such as these are structured. \$\endgroup\$ – IbrarMumtaz Jan 8 '14 at 14:14
  • \$\begingroup\$ I would like to see this example extended for IoC <3! \$\endgroup\$ – IbrarMumtaz Jan 8 '14 at 14:17
  • \$\begingroup\$ @shoe: is there a way you can have your repository on github? i have been searching for a good repository/uow pattern \$\endgroup\$ – Nick Kahn Jul 22 '14 at 2:15
  • \$\begingroup\$ @Abu I will see if I can create a few projects with a few designs that I've implemented over time and put them on my github page. \$\endgroup\$ – Shoe Jul 22 '14 at 3:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.