Tuesday, November 30, 2010

Entity framework creating a Generic Repository and UnitOfWork implementation – Part 1

The repository and unit of work patterns have gained a lot of acknowledgement from the agile development community and have gained lot of popularity in the recent years. In these series of posts I’ll explain how to create a repository and unit of work implementation using entity framework POCO development. I’ll be using the models from the samples used in my previous posts on the topic. In you have missed those, I would recommend to check out these samples before looking into this.
My attempt is to create a base repository class which handles the GRUD operations and entity specific repository implementations have the additional functionality that are specific to business requirements and inherit this base class. The unit of work implementation is borrowed from the concept described in the post by Faisal Mohamood in the ADO.NET team blogs.
Implement Unit Of Work pattern using ObjectContext
Unit of work implementation have two methods Save() and ExecuteCommandOnDb.
The Save method calls the SaveChanges on the context. ExecuteCommandOnDb method is mainly used in the test cases where we need to directly execute a SQL query on the data store.
public class UnitOfWork : ShoppingCartContext, EF4Recipies.Repository.IUnitOfWork
{
    private UnitOfWork() { }

    public static IUnitOfWork Default
    {
        get
        {
            return new UnitOfWork();
        }
    }

    public void Save()
    {
        SaveChanges();
    }

    public void ExecuteCommandOnDb(string sqlCommand)
    {
        this.ExecuteStoreCommand(sqlCommand);
    }
}

Defining the interface for Repositories
The IRepository interface defines the common functionality that can be implemented in the base repository class. It looks like
public interface IRepository where T : BaseEntity
{
    void Add(T entity);
    System.Collections.Generic.IEnumerable All();
    System.Collections.Generic.IEnumerable AllByCondition(Funcbool> predicate);
    T GetById(Guid id);
    T GetOneByCondition(Funcbool> predicate);
    void Remove(T entity);
    void Update(T entity);
}

The base repository class implements this interface.
public abstract class BaseRepository : EF4Recipies.Repository.IRepository where T : BaseEntity
{
    public BaseRepository(IUnitOfWork unitOfwork)
    {
        if (unitOfwork == default(ShoppingCartContext)) throw new ArgumentNullException("UnitOfWork cannot be null");
        Context = unitOfwork as ShoppingCartContext;
    }

    public abstract IEnumerable All();

    public T GetById(Guid id)
    {
        return All().SingleOrDefault(entity => entity.Id == id);
    }

    public T GetOneByCondition(Funcbool> predicate)
    {
        return All().SingleOrDefault(predicate);
    }

    public IEnumerable AllByCondition(Funcbool> predicate)
    {
        return All().Where(predicate);
    }

    public void Add(T entity)
    {
        entity.Id = entity.Id == Guid.Empty ? Guid.NewGuid() : entity.Id;
        entity.CreatedDate = entity.ChangedDate = DateTime.Now;
        ValidateAndAddToObjectSet(entity);
    }

    private void ValidateAndAddToObjectSet(T entity)
    {
        entity.Validate();
        var objectSet = All() as ObjectSet;
        objectSet.Context.AddObject(objectSet.EntitySet.Name, entity);
    }

    public virtual void Remove(T entity)
    {
        Context.DeleteObject(entity);
    }

    public virtual void Update(T entity)
    {
        var entityFromDb = GetById(entity.Id);
        if (entityFromDb != null && StructuralComparisons.StructuralEqualityComparer.Equals(entity.Timestamp, entityFromDb.Timestamp))
        {
            entity.ChangedDate = DateTime.Now;
            ValidateAndAddToObjectSet(entity);
        }
        else
            throw new InvalidOperationException("Concurrency exception");
    }
       
    protected ShoppingCartContext Context { get; private set; }
}

The Add and Update methods calls the validation on the entities that we have implemented in this post.
The update method also checks for concurrency issues before updating by comparing the timestamp field value with the entity object to be saved. Both remove and Update methods are implemented as Virtual because these need to be attached to the context from the class that inherits the base class. We’ll see the implementation of a CustomerRepository using the base repository we implemented in the upcoming posts.

No comments: