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

12 comments:

  1. Hi Diego,
    Maybe I've missed something, but I couldn't find the implementation of ArtistRepository in your example. Didn't you mean to use one general repository for all entities?

    Thanks.

    ReplyDelete
  2. Hi,

    I've added the ArtistRepository implementation, thank you for your comment.
    As mentioned above, 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).

    Diego

    ReplyDelete
  3. where is the code to download??

    ReplyDelete
  4. Hi Diego,

    Can you please give me some tips on how I can select specific columns (i.e. a query that only extracts few columns and not the whole table) using repository pattern above.

    Regards
    Parvez

    ReplyDelete
  5. Hi Parvez,

    The repository has a base class but it is implemented for each entity or group of entities (i.e ArtistRepository, AlbumRepository etc) , you can add a simple method to retrieve any combination of columns...

    Regards,
    Diego

    ReplyDelete
  6. Impresionante, la verdad que leer articulos como estos te alegran en mi caso la semana, un abrazo desde Argentina y segui publicando.

    ReplyDelete
  7. Gracias, es genial recibir comentarios, sobre todo buenos :-)
    siempre dispuesto a ayudar y compartir.
    happy coding
    Diego

    ReplyDelete
  8. Hi Diego,

    Could you please explain a little more about why client must be written in .NET with STE?
    The client and the service communicate via WCF service, so I don't see the point.

    Thanks,
    Dat

    ReplyDelete
    Replies
    1. Hi Nguyen,

      STE requires the client to track changes while in the client, this requires the entities to be more than just POCO entities, they will also contain some logic that will handle the state of each entiity (added, updated, deleted...), very much like datasets.
      Because of this you are sharing a reference of your entities with the client and not just passing the serialized data back and forward.
      Regards,
      Diego Resnik

      Delete
    2. Thank Diego for your answer. But, I could not understand what is the difference in your implementation here as it still use ChangeTracker on the client. In my understanding, I think that the only way to build cross platform client is to use DTO instead of self tracking entity.
      Please correct me if I'm wrong. By the way, is there any link to the sample code?

      Thanks,
      Dat

      Delete
    3. Hi,
      When you say "...I could not understand what is the difference in your implementation..." - to what are you comparing it?
      As I mentioned in the article above, if your client is not .net, STE is not an option - if your client is not .net and/or is not in your full control, than you can just transfer the data without OOP rules and without behavior and/or track changes capabilities (=DTO).
      Regards,
      Diego

      Delete
  9. Ok I understand now. Thank you so much, Diego.

    ReplyDelete