Thursday, April 7, 2011

EF4 Self Tracking Entities & Repository design pattern

Today I'm going to talk about a new sample project I was building these days just to get to know Entity Framework 4.0 better.

After reading Mark Mishaev's post here (Building N-Tier Applications with Entity Framework 4) & a few of his references I decided to explore the self tracking entities.

As I see it, Self tracking entities is one of the most basic features the previous version of EF was missing.

Self tracking entities as the name implies is the ability of our entities to contain their state, if you're familiar with datasets & their diffgram capability than you probably used the RowState property to detect which rows are new, which are updated, deleted or unchanged - that is what tracking means.

With STE (self tracking entities) you can send your entities to your client & when receiving it back, detect easily all the changes your client made and save it to your database (of course after choosing the right concurrency strategy for your application), without it - you'll find yourself comparing the returned entity with your database to decide which changes were made & setting their state one by one before you can actually save.

So STE is the ultimate solution? Not for sure....one of the big disadvantages of STE is the fact that your client must know the actual entity - meaning:
1. It will work only if you code both the server & client (it's not always the case), proxies passing the entity's datamembers won't do the tracking stuff...
2. Your client must be .net client.
3. Any changes to entity structure/logic will require client publishing (a big minus architecturally speaking).

If you're absolutely sure you can ignore these disadvantages in your specific application - you will gain a simple & great way to track changes out of the box.

Searching for the best way to implement the use of EF I encounter many discussions about a few related patterns, the two that repeatedly stood out were Repository & UnitOfWork.

To make a long reading short..

Repository will help us separating our business logic layer from knowing anything about entity framework - potentially can allow us to control our dataAccess layer behavior (cache, trace, security etc) better and change the implementation without changing the whole application, it will also allow us to replace the repository with an in-memory repository which can be a great way to unit test our application without a database.

namespace Dieg.Framework.DataAccess
{
    public interface IRepository
    { 
        T GetById(int id);
        IEnumerable GetAll();
        IEnumerable Query(Expression<Func<T, bool>> filter);
        void Add(T entity);
        void Remove(T entity);

        void ApplyChanges(T entity);
    }
}

Implementation:

namespace Dieg.Framework.DataAccess
{
    public abstract class Repository : IRepository where T : class
    {
        protected IObjectSet _objectSet;
        protected IUnitOfWork _uow;

        public Repository(IUnitOfWork uow) 
        {
            _uow = uow;
            _objectSet = _uow.CreateObjectSet();
        }

        public abstract T GetById(int id);

        public IEnumerable GetAll()
        {
            return _objectSet;
        }

        public IEnumerable Query(System.Linq.Expressions.Expression<Func<T, bool>> filter)
        {
            return _objectSet.Where(filter);
        }

        public void Add(T entity)
        {
            _objectSet.AddObject(entity);
        }

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

        public abstract string Name
        {
            get;
        }

        public abstract void ApplyChanges(T entity);
    }
}

Implementing a specific repository, I prefer implementing a separate repository for each entity this way we can choose a unique behavior for each entity (for example: not all entities allow all CRUD operations, maybe there are different authorization rules for some entities etc).

namespace Dieg.MusicLibrary.DataAccess
{
    public class ArtistRepository:Repository
    {
        public const string ENTITY_NAME = "Artist"; 

        public ArtistRepository(UnitOfWork uow):base(uow)
        { }

        public override Artist GetById(int id)
        {
            return _objectSet.SingleOrDefault(a => a.Id == id);
        }

        public override string Name
        {
            get { return ENTITY_NAME; }
        }


        public override void ApplyChanges(Artist entity)
        {
            _uow.ApplyChanges(Name, entity);
        }
    }
}

UnitOfWork - according to Martin Fowler, the Unit of Work pattern "maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems." (The Unit Of Work Pattern And Persistence Ignorance).

namespace Dieg.Framework.DataAccess
{
    public interface IUnitOfWork
    {
        IObjectSet CreateObjectSet() where T : class;

        void SaveChanges();

        void ApplyChanges(string entityName, object entity);
    }
}

ApplyChanges method will contain the STE implementation of updating our entities state, since ApplyChanges is per entity, we can control in our BL which entities should be influenced by the specific BL method and avoid saving irrelevant changes.

Notice the implementation will have to be in the dataAcess layer project & not part of the 'framework/core/lib', since we want it to use the STE specific context extensions.

namespace Dieg.MusicLibrary.DataAccess
{
    public class UnitOfWork:IUnitOfWork, IDisposable
    {
        private readonly DiegMusicLibraryContainer _context;

        public UnitOfWork()
        {
            _context = new DiegMusicLibraryContainer();
        }
        
        public void SaveChanges()
        {
            _context.SaveChanges();
        }

        public void Dispose()
        {
            _context.Dispose();
        }

        public IObjectSet CreateObjectSet() where E : class
        {
            return _context.CreateObjectSet();
        }

        public void ApplyChanges(string entityName, object entity)
        {
            if (entity is IObjectWithChangeTracker)
            {
                _context.ApplyChanges(entityName, (IObjectWithChangeTracker)entity);
            }
            else
            {
                throw new ArgumentException("entity must implement IObjectWithChangeTracker to use applyChanges");
            }
        }
    }
}


As I mentioned earlier, when using STE the client must have a reference to the entities, so we'll have to separate the entities from the EF context & edmx code, this can easily done using T4 templates (see :How to Separate Self-Tracking Entities to Their Own Class Library (by Gil Fink)).

Let's code a simple test to see the basic concept:

static void Main(string[] args)
        {
            Artist artist;
            
            using (UnitOfWork uow = new UnitOfWork())
            {
                ArtistRepository Artists = new ArtistRepository(uow);

                foreach (var Artist in Artists.GetAll())
                {
                    Console.WriteLine(Artist.Name);
                }
                
                artist = Artists.GetById(1);

            }

            /*unitOfWork (which holds the objectContext) is disposed here
            this will happen also when sending objects through a
              WCF (or similar) service
            */

            //change something...
            artist.ChangeTracker.ChangeTrackingEnabled = true;
            artist.Name = string.Concat(artist.Name,"AAA");


            using (UnitOfWork uow2 = new UnitOfWork())
            {
                ArtistRepository Artists = new ArtistRepository(uow2);

                //calling ApplyChanges will update the state of the artist
                //using behind the scense the STE Changetracker
                //without this the save won't recognize any changes -
                // comment the following line & try it out!!
                Artists.ApplyChanges(artist);

                uow2.SaveChanges();

                Artist b = Artists.GetById(1);
                Console.WriteLine(b.Name);
            }

            Console.ReadLine();
        }

Good luck!
Diego