6
\$\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.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Oct 31, 2013 at 18:15
\$\endgroup\$
1
  • 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\$ Commented Oct 31, 2013 at 19:50

1 Answer 1

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.

answered Oct 31, 2013 at 18:39
\$\endgroup\$
8
  • \$\begingroup\$ A nice addition to this would be Ninject too, good answer though \$\endgroup\$ Commented Nov 1, 2013 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\$ Commented Jan 8, 2014 at 14:14
  • \$\begingroup\$ I would like to see this example extended for IoC <3! \$\endgroup\$ Commented Jan 8, 2014 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\$ Commented Jul 22, 2014 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\$ Commented Jul 22, 2014 at 3:30

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.