Entity Framework - Generic repository Pattern - Part 1

  • Part 1 - Generic Repositry Pattern
  • Part 2 - Generic Repositry Pattern Contd.

The generic repository pattern has been a matter of discussion on the forums and SO. Developers are not convinced that it solves a problem, yet everyday SO has more questions about the pattern. Clearly, something is not right. I tried to implement the pattern in a way I thought fit. I changed my mind a million times during the process, that tells you that the process was not smooth. You can find several implementations of the pattern scattered all over the internet. I will not pass a opinion on the pattern. I would rather tell you what issues I faced and I how solved (or not) them. First I created an IRepository and IUnitOfwork

public interface IRepository
{
    T Add(T entity);
    T Remove(T entity);
    T Update(T entity);
    IQueryable Query();        
}
public interface IUnitOfWork : IDisposable
{
    void Commit();
}

Next, let's get the EF specific implementation up and running.

/// <summary>
/// A EFRepository represents the repository for performing operations on the
/// Entity using the EntityFramework.
/// </summary>
/// <typeparam name="T">T is the Entity</typeparam>
public class EFRepository<T> : IRepository<T> where T : class
{
    /// <summary>
    /// This is set in the constructor and provides access to the underlying EntityFramework methods
    /// </summary>
    private DbSet<T> _dbSet;
    /// <summary>
    /// The context for working with the EntityFramework. This is set in the constructor.
    /// </summary>
    private DbContext _dataContext;
    /// <summary>
    /// Initialises a new instance of Repository for <see cref="T"/>
    /// </summary>
    /// <param name="unitOfWork">IUnitOfWork</param>
    /// <param name="dataContext">DbContext</param>
    /// <exception cref="ArgumentNullException">Throws ArgumentNullException if any of the arguments is null</exception>
    public EFRepository(IUnitOfWork unitOfWork, DbContext dataContext)
    {
        if (unitOfWork == null)
        {
            throw new ArgumentNullException("unitOfWork", "unitOfWork cannot be null");
        }
        if (dataContext == null)
        {
            throw new ArgumentNullException("dataContext", "dataContext cannot be null");
        }
        var EfUnitOfWork = unitOfWork as EFUnitOfWork;            
        _dataContext = dataContext;
        _dbSet = _dataContext.Set<T>();
    }
    /// <summary>
    /// Adds the specified Entity to the DbSet of the context.
    /// The Entity is inserted only when UnitOfWork is commited.
    /// </summary>
    /// <param name="item">The Entity to be added</param>
    /// <returns>The added Entity</returns>
    public T Add(T item)
    {
        return _dbSet.Add(item);
    }
    /// <summary>
    /// Removes the specified Entity from the DbSet of the context.
    /// The Entity is removed only when UnitOfWork is commited.
    /// </summary>
    /// <param name="item">The Entity to be removed</param>
    /// <returns>The Entity removed from the underlying DbSet</returns>
    public T Remove(T item)
    {
        return _dbSet.Remove(item);
    }
    /// <summary>
    /// Removes the specified Entity from the DbSet of the context.
    /// The Entity is removed only when UnitOfWork is commited.
    /// </summary>
    /// <param name="item">The Entity to be updated</param>
    /// <returns>the Entity removed from the underlying DbSet</returns>
    public T Update(T item)
    {
        var updated = _dbSet.Attach(item);
        _dataContext.Entry(item).State = EntityState.Modified;
        return updated;
    }
    /// <summary>
    /// Provides the caller with the underlying DbSet.
    /// </summary>
    /// <returns>An IQueryable to run queries against the underlying DbSet</returns>
    public IQueryable<T> Query()
    {
        return _dbSet;
    }

Follow this up with an implementation of EFUnitOfWork

/// <summary>
/// Represents an IUnitOfWork for Entity Framework
/// </summary>
public class EFUnitOfWork : IUnitOfWork
{
    private DbContext _context;        
    private bool _disposed;
    public EFUnitOfWork(DbContext context)
    {
        if (context == null)
        {
            throw new UnitOfWorkException();
        }
        _context = context;
    }
    /// <summary>
    /// Method to be called when a UnitOfWork is to be committed.
    /// </summary>
    public void Commit()
    {
        _context.SaveChanges();
    }
    // Implement IDisposable.       
    public void Dispose()
    {
        Dispose(true);
        // Take yourself off the Finalization queue to prevent finalization code for object from executing a second time.
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if (!_disposed)
        {
            // If disposing equals true, dispose all managed and unmanaged resources.
            if (disposing)
            {
                // Dispose managed resources.
                if (_context != null)
                {
                    _context.Dispose();
                }
            }             
        }
        _disposed = true;
    }
}
[Serializable]
public class UnitOfWorkException : Exception
{
    public override string Message
    {
        get
        {
            return "The parameter must be EFUnitOfWork";
        }
    }
}

The above code is at Github

Are there alternatives ? Oh yes plenty. Some just differ in implementation details , but others differ in philosophy altogether. Next time I will blog about a different one that I thought of initially. Then later I will write about the problems we face. Pick and choose what fits.

comments powered by Disqus