2
votes

UPDATE 3:

I see this video and how the author emphasize to use Repository/UOW...as oppose to what i discouraging. btw author is using ORM(EF)

http://pluralsight.com/training/Courses/TableOfContents/spa

UPDATE 2:

As I was playing with the Repository and I have this scanrio to tackle and I'm not sure if I'm following the right direction... So in my controller:

public class PersonsController : Controller
{
    GenericRepository<Person> _genericRepository = new GenericRepository<Person>(new PersonsContext()); 

    public ActionResult Index()
    {
        GenericRepository<Actors> _genericActorRepository = new GenericRepository<Actors>(new PersonsContext());
        IEnumerable<Actors> _actorList = _genericActorRepository.GetAll();
        //IList<Actors> _actorList1 = _genericActorRepository.GetAll().ToList();
        ViewBag.ActorList = new SelectList(_actorList);
        return View(_genericRepository.GetAll());
    }

}

UPDATE:

Here is the link of Microsoft Developer Network talks about GenericRepository!

I am trying to implement best practices during the design phase of a system. I am going to be using Entity Framework, ASP.NET MVC 5 C#, and the generic repository/unit of work pattern (hopefully).

My question: how do I introduce Unit Of Work in my GenericRepository?

Here is my GenericRepository class:

public interface IGenericRepository<TEntity> : IDisposable
{
    Task<TEntity> GetByIdAsync(int id);        
    IQueryable<TEntity> SearchFor(Expression<Func<TEntity, bool>> predicate);
    IQueryable<TEntity> GetAll();
    Task EditAsync(TEntity entity);
    Task InsertAsync(TEntity entity);
    Task DeleteAsync(TEntity entity);
}  

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    protected DbSet<TEntity> _dbSet;
    private readonly DbContext _dbContext;

    public GenericRepository(DbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = _dbContext.Set<TEntity>();
    }

    public GenericRepository() {}

    public IQueryable<TEntity> GetAll()
    {
        return _dbSet;
    }

    public async Task<TEntity> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }

    public IQueryable<TEntity> SearchFor(Expression<Func<TEntity, bool>> predicate)
    {
        return _dbSet.Where(predicate);
    }

    public async Task EditAsync(TEntity entity)
    {
        _dbContext.Entry(entity).State = EntityState.Modified;
        await _dbContext.SaveChangesAsync();
    }

    public async Task InsertAsync(TEntity entity)
    {

        _dbSet.Add(entity);
        await _dbContext.SaveChangesAsync();
    }

    public async Task DeleteAsync(TEntity entity)
    {
        //if (context.Entry(entityToDelete).State == EntityState.Detached)
        //{
        //    dbSet.Attach(entityToDelete);
        //}
        _dbSet.Remove(entity);
        await _dbContext.SaveChangesAsync();
    }

    public void Dispose(bool disposing)
    {
        if (_dbContext != null)
        {
            _dbContext.Dispose();
        }
        GC.SuppressFinalize(this);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Model class:

public class Person
{
    public int Id { get; set; }
    public String Fullname { get; set; }
    public String Profession { get; set; }
    public int Age { get; set; }
}

Context:

public class PersonsContext : DbContext
{
    public PersonsContext() : base("name=PersonsContext")
    {
    }
    public DbSet<Person> People { get; set; }
}

Controller:

public class PersonsController : Controller
{
    GenericRepository<Person> _genericRepository = new GenericRepository<Person>(new PersonsContext()); 
    //
    // GET: /Persons/
    public ActionResult Index()
    {
        return View(_genericRepository.GetAll());
    }

    //
    // GET: /Persons/Details/5
    public async Task<ActionResult> Details(Int32 id)
    {
        Person person = await _genericRepository.GetByIdAsync(id);
        if (person == null)
        {
            return HttpNotFound();
        }
        return View(person);
    }

    //
    // GET: /Persons/Create
    public ActionResult Create()
    {
        return View();
    }

    //
    // POST: /Persons/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Create(Person person)
    {
        if (ModelState.IsValid)
        {
            await _genericRepository.InsertAsync(person);
            return RedirectToAction("Index");
        }

        return View(person);
    }

    //
    // GET: /Persons/Edit/5
    public async Task<ActionResult> Edit(Int32 id)
    {
        Person person = await _genericRepository.GetByIdAsync(id);
        if (person == null)
        {
            return HttpNotFound();
        }
        return View(person);
    }

    //
    // POST: /Persons/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Edit(Person person)
    {
        if (ModelState.IsValid)
        {
            await _genericRepository.EditAsync(person);
            return RedirectToAction("Index");
        }
        return View(person);
    }
}
4
All are great answers and I'm learning something new as oppose to what I have been reading on Microsoft Developer Network, I'm not sure why the authors (blogs/site...) raised up the awareness as when and why you should think of implementing Repository/UOF or Service Layer...uffNick Kahn
One thing to keep in mind is that most of the Microsoft people that publish samples aren't in the business of writing real world code most of the time. They're usually support and/or evangelist type people. It's their job to "show off" the tools and what they can do, not necessarily to tell you exactly how to write large maintainable projects. This is one of the cases where the Microsoft demo machine latched onto something (uow/gr) and they keep publishing new articles about it. Both uof and gr are both good patterns, which is why EF implements them. But no reason to do it yourself.Erik Funkenbusch

4 Answers

9
votes

This has largely become an anti-pattern in my opinion. Using a Unit of Work and Repository is important and good design, BUT most people overlook the fact that Entity Framework is already an implementation of Unit of Work and Generic Repository.

Only implement your own UoW and Generic Repository on top of EF if you truly feel it's necessary (say, you intend to support different kinds of data access) or you believe your application will be exceptionally long lived and require a lot of ongoing maintenance. If your app is relatively simple, and/or is unlikely to change a lot, then there is no reason to implement additional abstractions.

I don't suggest directly using EF code in your controller, so you should use some kind of data abstraction, such as a service layer or concrete repository (as opposed to generic, a concrete repository has methods like GetCustomers() that abstracts the business logic into a method).

Another reason to implement your own UoW or Generic Repository is if you're not going to use an ORM that supports UoW, in that case you would need your own implementation.

2
votes

Generally, avoid including unnecessary abstractions to your code just because you read somewhere it's best practice. If you are not able to justify abstractions, you are better avoiding them. You can easily refactor your code later and add as many layers as required. It's a bad idea to over complicate your implementation if your answer is maybe I will need that in future.

1
votes

I think it's typically an anti-pattern to layer a generic repository over Entity Framework. The Entity Framework is your generic repository, and it already implements the Unit-of-Work pattern. In my own code, I skip the "repository" layer (since I'm using EF), and instead either call directly into the EF code from my controllers, or if the logic is complex or needs to be used in multiple places, I'll add in a Services layer over the top of EF which has actual business logic (i.e., if you add 'x', you always need a 'y' to go along with it, that sort of thing). The MVC code - and code from other places too - will then call into the Services layer, but the Services layer is not and can't be generic.

The reason why you sometimes used to implement a generic repository over EF was for testability - it was a lot easier to mock out your own generic repository than to mock out EF. But these days, EF is reasonably mockable - no sniggers please - so long as you're careful about your Include() statements and a few other gotchas along the way.

1
votes
public class UnitOfWork
{
    private DbContext _dbContext = new MyNameSpace.MyDbContext();
    private readonly bool _readOnly;

    public UnitOfWork(bool readOnly = false)
    {
        _readOnly = readOnly;
    }

    public void Commit()
    {
        _dbContext.SaveChanges();
    }

    internal DbContext GetDbContext()
    {
        return _dbContext;
    }

    internal bool ReadOnly
    {
        get
        {
            return _readOnly;
        }
    }
}

Also, a generic repository like:

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    private DbSet<TEntity> _dbSet;
    private DbQuery<TEntity> _dbQuery;
    private readonly DbContext _dbContext;

    public GenericRepository(UnitOfWork unitOfWork)
    {
        _dbContext = unitOfWork.GetDbContext();
        _dbSet = _dbContext.Set<TEntity>();
        if (unitOfWork.ReadOnly)
        {
            _dbQuery = _dbSet.AsNoTracking();
        }
        else
        {
            _dbQuery = _dbSet;
        }
    }

    public GenericRepository()
    {
    }

    public IQueryable<TEntity> GetAll()
    {
        return _dbQuery;
    }

    public async Task<TEntity> GetByIdAsync(int id)
    {
        return await _dbSet.FindAsync(id);
    }

    public IQueryable<TEntity> SearchFor(Expression<Func<TEntity, bool>> predicate)
    {
        return _dbQuery.Where(predicate);
    }

    public async Task EditAsync(TEntity entity)
    {
        _dbContext.Entry(entity).State = EntityState.Modified;
        await _dbContext.SaveChangesAsync();
    }

    public async Task InsertAsync(TEntity entity)
    {
        _dbSet.Add(entity);
        await _dbContext.SaveChangesAsync();
    }

    public async Task DeleteAsync(TEntity entity)
    {
        _dbSet.Remove(entity);
        await _dbContext.SaveChangesAsync();
    }
}

If you decide later that you want a lightweight ORM, you are not glued to it.

You can migrate your IQueryable references to specific methods on your repository that do not couple as tightly, and port the EF generated SQL to the lightweight ORM, or direct ADO.Net that you prefer.