2
\$\begingroup\$

I am working on a generic repository. And I wondered if anyone could give some feedback on it.

Interface for DbContext:

public interface IDbContext
{
    IDbSet<T> Set<T>() where T : class;
    DbEntityEntry Entry<T>(T entity) where T : class;
    void SaveChanges();
}

Context class

public class BookContext : DbContext, IDbContext
{
    public BookContext()
        : base("BookDB")
    { }

    public new IDbSet<T> Set<T>() where T : class
    {
        return base.Set<T>();
    }

    public new DbEntityEntry Entry<T>(T entity) where T : class
    {
        return base.Entry(entity);
    }

    public new void SaveChanges()
    {
        base.SaveChanges();
    }
}

Repository interface

public interface IRepository<T>
{
    IQueryable<T> FindAll();
    IQueryable<T> Find(Expression<Func<T, bool>> predicate);
    T FindById(int id);
    void AddOrUpdate(T entity);
    void Remove(T entity);
}

Repository implementation

public class Repository<T> : IRepository<T>
    where T : class, IEntity
{
    protected readonly IDbContext Context;

    public Repository(IDbContext context)
    {
        Context = context;
    }

    public IQueryable<T> FindAll()
    {
        return Context.Set<T>();
    }

    public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return Context.Set<T>().Where(predicate);
    }

    public T FindById(int id)
    {
        return Context.Set<T>().SingleOrDefault(o => o.Id == id);
    }

    public void AddOrUpdate(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        if (entity.Id == 0)
        {
            Context.Set<T>().Add(entity);
        }
        else
        {
            Context.Entry(entity).State = EntityState.Modified;
        }

        Context.SaveChanges();
    }

    public void Remove(T entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        Context.Set<T>().Remove(entity);
        Context.SaveChanges();
    }
}

Additional repository

public interface IBookRepository
{
    Book FindByIsbn(string isbn);
    IQueryable<Book> FindByAuthor(Author author);
}

Book repo implementaion

public class BookRepository : Repository<Book>, IBookRepository
{
    public BookRepository(IDbContext context) : base(context)
    {
    }

    public Book FindByIsbn(string isbn)
    {
        return Context.Set<Book>().SingleOrDefault(b => b.Isbn.Equals(isbn));
    }

    public IQueryable<Book> FindByAuthor(Author author)
    {
        return Context.Set<Book>().Where(b => b.Author.Id == author.Id);
    }
}

I am planning to use this repo with autofac IoC. I am not sure If I should use Unit of work pattern and should I make my repos disposable. Any feedback would be appreciated.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

1) I would not expose IQueryable via IRepository. IQueryable is still a virtual set of objects that hasn't been retrieved from a database yet. If you have (for example) some problem with database performance, you would have to go through all your application to find out what queries you construct and send to DB.

Instead I would expose methods for specific purposes on specific repositories. These methods would return IList<T> (or ICollection<T>, depends on your preference). Then you have all the code for accessing a database on a single place. For example, your IBookRepository would look like:

public interface IBookRepository : IRepository<Book>
{
    Book FindByIsbn(string isbn);
    IList<Book> FindByAuthor(int authorId);
    IList<Book> FindByTitle(string title);
    ...
}

And your generic repository would be:

public interface IRepository<T>
{
    T FindById(int id);
    void AddOrUpdate(T entity);
    void Remove(T entity);
}

2) It is not necessary to make your repositories disposable. So far you have nothing to dispose there. Your repositories get IDbContext (which is IDisposable) from outside, thus it should not be disposed inside a repository. If your repositories get created via an IoC container, the container will also handle disposing of all dependencies.

3) UnitOfWork: You can implement UnitOfWork pattern if you want to (e.g.) wrap all your database work inside transactions. I would recommend such IUnitOfWork interface:

public interface IUnitOfWork
{
    void SaveChanges();
    void BeginTransaction();
    void CommitTransaction();
    void RollbackTransaction();
}

Implemetation of IUnitOfWork would have dependency on IDbContext. Then the only places where you work with a database (via IDbContext) are UnitOfWork and Repository classes. At places where you want to save your work to a DB, use IUnitOfWork, not IDbContext.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Isn't something like FindByIsbn typically done from a service layer? \$\endgroup\$
    – user9993
    Mar 30, 2016 at 8:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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