Friday, April 9, 2010

WCF RIA, Repository pattern and testability

When you developing WCF RIA domain services you usually highly coupled with underlying data access layer (DAL) technology of your choice. It’s usually Entity Framework (EF), LinqToSQL, nHibernate, pure ADO.NET or other ORM/DAL frameworks. It’s great, however it brings following issues

  1. Learning curve for some ORM/DAL frameworks can be painful (it may include convoluted APIs with leaking abstractions)
  2. Due to coupling it’s hard/impossible to switch to new DAL framework if you bump into some major issues with you current one
  3. Lack of testability with some of ORM/DAL frameworks

That’s why it makes sense to introduce another level of abstraction/indirection between your domain logic and DAL. It must be simple, testable and well known concept. Well, and we have one – it’s famous Repository pattern.

There are many ways you can define/implement it, let’s consider my version. In this example we will implement generic repository for EF’s entities. First of all – let’s define abstraction

Code Snippet
public interface IRepository<T> where T : EntityObject
{
    // Query
    IQueryable<T> Query { get; }

    // CRUD
    void Insert(T entity);
    void Update(T entity);
    void Delete(T entity);
}

As you can see our repository is quite succinct. It can do two things – Query for data and CRUD. And it seems that learning curve for using this abstraction is minimal (read - developers will love it)

As long as we trying to adapt standard code generated by EF designer for our repository needs we need to introduce some more interfaces to abstract away our custom ObjectContext

Code Snippet
public interface IUnitOfWork
{
    int SaveChanges();
}

This is abstraction for EF’s ObjectContext which is essentially implementation of UnitOfWork (UoW) pattern. However it’s not only UoW, it’s also entity sets container, which can be abstracted away by

Code Snippet
public interface IObjectContext : IUnitOfWork
{
    IObjectSet<T> CreateObjectSet<T>() where T : class;
}

ok, so far so good, now we need to make our custom ObjectContext (generated by EF designer) to implement this abstraction. It can be easily done by partial class like this

Code Snippet
public partial class TestEntities : IObjectContext
{
    public new IObjectSet<T> CreateObjectSet<T>() where T : class
    {
        return base.CreateObjectSet<T>();
    }
}

great, now we can implement our generic repository

Code Snippet
/// <summary>
/// Base repository, provides basic CRUD, Query and IUnitOfWork
/// Be aware, that unit of work is shared across all repositories,
/// so first SaveChanges() will save all changes made by all repositories before
/// </summary>
/// <typeparam name="T">Type of entity you working with</typeparam>
public class RepositoryBase<T> : IRepository<T>, IUnitOfWork where T : EntityObject
{
    private readonly IObjectContext _context;
    private readonly IObjectSet<T> _objectSet;

    public RepositoryBase()
    {
        if (DataContext.Current == null) throw new Exception("DataContext is undefined");
        _context = DataContext.Current;
        _objectSet = _context.CreateObjectSet<T>();
    }

    #region IRepository<T> Members

    public IQueryable<T> Query
    {
        get { return _objectSet; }
    }

    public virtual void Insert(T entity)
    {
        if (entity.EntityState == EntityState.Added) return;
        if (entity.EntityState == EntityState.Unchanged)
        {
            var ctx = _context as ObjectContext;
            if (ctx == null) return;
            ctx.ObjectStateManager.ChangeObjectState(entity, EntityState.Added);
        }
        else
        {
            _objectSet.AddObject(entity);
        }
    }

    public virtual void Update(T entity)
    {
        var ctx = _context as ObjectContext;
        if (ctx == null) return;
        if (entity.EntityState == EntityState.Detached) ctx.Attach(entity);
        ctx.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }

    public virtual void Delete(T entity)
    {
        if (entity.EntityState == EntityState.Detached) _objectSet.Attach(entity);
        _objectSet.DeleteObject(entity);
    }

    #endregion

    #region IUnitOfWork Members

    public virtual int SaveChanges()
    {
        // Update common fields
        var ctx = _context as ObjectContext;
        if (ctx != null)
        {
            var changes = ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified);
            foreach (var entity in changes.Select(objectStateEntry => objectStateEntry.Entity))
            {
                UpdateHelper.UpdateCommonFields(entity);
            }
        }
        return _context.SaveChanges();
    }

    #endregion
}

As you can see our generic repository mixes Repository and UnitOfWork patterns, which will allow us to do batch data modifications and save in one go.

The usage of this generic class is simple, elegant and straightforward

Code Snippet
var orderRepository = new RepositoryBase<Order>();
var order = orderRepository.Query.First(o => o.ID == id);
order.Name = "Modified on server!";
orderRepository.SaveChanges();

You may wonder what is that magical DataContext our Repository depends on? Well, wait for the next post ;)

to be continued…

5 comments:

  1. Sergey,

    How can we reach you?

    Jack
    info@axeo.com

    ReplyDelete
  2. Cool stuff here but a bit hard to follow for a beginner like me.

    May I ask you to provide working sample projects from time to time... Unfortunately I'm unable to make work any of your cool looking technics...

    Thanks,
    John.

    ReplyDelete
  3. hi excellent code 2 things though:

    1) i take ur constructor repositorybase is for linqtosql, what would be the equivalent for wcfria-ef, as DataContext is not recognised?
    2)what was the purpose of UpdateHelper.UpdateCommonFields(entity);?

    Many Thanks

    ReplyDelete