Nick Kahn
Nick Kahn

Reputation: 20078

Implementing Unit Of Work using GenericRepository with MVC 5 /EF

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

Upvotes: 2

Views: 4476

Answers (4)

DaveMorganTexas
DaveMorganTexas

Reputation: 907

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.

Upvotes: 1

Erik Funkenbusch
Erik Funkenbusch

Reputation: 93464

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.

Upvotes: 9

korhner
korhner

Reputation: 733

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.

Upvotes: 2

Ken Smith
Ken Smith

Reputation: 20445

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.

Upvotes: 1

Related Questions