Generic IQueryable Repository for Entity Framework 4 (EF4)

Tags: c#, linq, Entity Framework, EF4

Anyone who has tried to create tests with Entity Framework (EF) knows that EF is not very unit test friendly. I needed something simple, reusable and unit testable. this generic repository was the result.

The goal of this project was high reusability, ease of implementation and to be unit tests friendly.

There are a lot of Repository Pattern's out there that hide a lot of the underlying functions of your data access layer (in this case LINQ). Well, I really like LINQ to SQL and didn't want to hide any of that. Plus, if you really need encapsulation it should be done in your service layer and not your repository layer.

Some Quick Examples

remember to add "using System.Linq"

IRepository<Log> logs = new Repository<Log>(new MyDataContext());

// use LINQ
var items = from l in logs.All()
            where l.LogType == "Error"
            orderby l.LogId descending
            select l;

// load an item
var item = logs.All().Single(l => l.LogId == 1);

// update an item
item.TimeStamp = DateTime.Now;
logs.Save();

// create an item
item = new Log {
    TimeStamp = DateTime.Now,
    LogType = "Information",
    Message = "This is a new Log!"
};

logs.Add(item);
logs.Save();

// delete an item
logs.Remove(item);

// Eager Loading
items = from l in logs.All("Details")
        select l;

Repository Code

The code is pretty simple, it just looks long because of all the Argument Validation...

public interface IRepository<E>
{
    IQueryable<E> All();
    IQueryable<E> All(string includes);
    void Add(E entity);
    void Remove(E entity);
    void Update(E entity);
    void Save();
}

public class Repository<E> : IRepository<E> where E : EntityObject
{
    protected readonly ObjectContext context;
    protected readonly ObjectSet<E> objectSet;

    public Repository(ObjectContext context)
    {
        #region Argument Validation

        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        #endregion

        this.context = context;
        this.objectSet = context.CreateObjectSet<E>();
    }

    public virtual IQueryable<E> All()
    {
        return objectSet;
    }

    public virtual IQueryable<E> All(string includes)
    {
        ObjectQuery<E> value = objectSet;
        if (String.IsNullOrEmpty(includes))
        {
            foreach (var includeProperty in includes.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                value = value.Include(includeProperty.Trim());
            }
        }
        return value;
    }

    public virtual void Add(E entity)
    {
        #region Argument Validation

        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        #endregion

        objectSet.AddObject(entity);
    }

    public virtual void Remove(E entity)
    {
        #region Argument Validation

        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        #endregion

        if (entity.EntityState == EntityState.Detached)
        {
            objectSet.Attach(entity);
        }

        objectSet.DeleteObject(entity);
    }

    public void Save()
    {
        context.SaveChanges();
    }
}

Unit Testing

Being able to write Unit Tests is a must and was one of the main goals of creating this repository. With this class, writing unit tests has become much more simple by creating a MemoryRepository.

public class MemoryRepository<E> : IRepository<E> where E : EntityObject
{
    protected readonly List<E> objectSet = new List<E>();

    public virtual IQueryable<E> All()
    {
        return objectSet.AsQueryable();
    }

    public IQueryable<E> All(string includes)
    {
        return All();
    }

    public virtual void Add(E entity)
    {
        objectSet.Add(entity);
    }

    public virtual void Remove(E entity)
    {
        objectSet.Remove(entity);
    }

    public virtual void Update(E entity)
    {
    }

    public void Save()
    {
    }
}

Here's an example test that shows how to populate the MemoryRepository.

[TestMethod]
public void MemoryRepository_can_Add_entities()
{
    // Arrange
    IRepository<Category> value = new MemoryRepository<Category>();
    value.Add(new Category { Category_ID = 1, Category_Name = "Beverages" });
    value.Add(new Category { Category_ID = 2, Category_Name = "Condiments" });
    value.Add(new Category { Category_ID = 3, Category_Name = "Confections" });

    // Act
    int result = value.All().Count();

    // Assert
    Assert.AreEqual(3, result);
}

Be sure to check out Part 2: Repository and Unit of Work for Entity Framework (EF4) for Unit Testing

Add a Comment