Repository Pattern in Entity Framework 4.0

This is a bit of a ‘me too’ post…  Reason for posting this is it continues the story of building an application.  Also, I will be posting up how I extended the caching aspect to support caching for entity framework (this wouldn’t make sense without this post on the repository).  Caching is not currently supported by Microsoft so you have to roll your own (this is a shame because other ORM frameworks do support caching).  More on that later.  It also shows why the object lifecycle and guaranteed disposal was so important (solved by Windsor Lifestyles, as per my posts on WCF and NServiceBus per message lifestyles).

This post doesn’t just cover the repository pattern, it also shows the specification and unit of work pattern in use.  The specification pattern is actually described in tandem with the repository pattern by Martin Fowler.

Entity Framework 4.0 supports POCOs (plain old CLR objects).  Right now our project isn’t using this.  The project was already underway and using EF and I see no reason to change anything right now.  I would probably have chosen NHibernate over entity framework, but that’s my personal preference.  To be honest Entity Framework 4.0 is much better than the previous version.  This isn’t a debate about which is better.  But, if a repository pattern is used you could (if you wanted to…) change.  With that in mind:

public interface ILinqRepository
{
    ISession Session { get; }
    void Add(object entity);
    void Update(object entity);
    void Delete(object entity);
    void Attach(object entity);
    object GetAttachedItem(object entity);
    void Detach(object entity);
    void SaveChanges();
}
 
public interface ILinqRepository<T> : ILinqRepository, ISpecificationQueryable<T>
    where T : class
{
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
    void Attach(T entity);
    T GetAttachedItem(T entity);
    void Detach(T entity);
    T GetOrAdd(ILinqSpecification<T> specification, Func<T> create);
}
 
// Note:  The specification queryable interface wasn't there originally, I separated it because I needed it somewhere else - I will probably show that other use later.
 
    public interface ISpecificationQueryable<T>
        where T : class
    {
        T FindSingle(ILinqSpecification<T> specification);
        IQueryable<T> FindAll(ILinqSpecification<T> specification);
    }
 

These interfaces don’t know about entity framework.  They define queries as LINQ specifications so as long as the implementation supports LINQ then you’re all good.  I guess there is an IRepository which has no LINQ stuff that this could implement, but right now I don’t need it.  There are a few things on the repository that were added later to support some stuff I needed to do with caching, hopefully become clear later.

The entity framework implementation looks like this:

public interface IEntityFrameworkLinqRepository<TEntity> : ILinqRepository<TEntity>
        where TEntity : class
    {
        new IEntityFrameworkSession Session { get; }
        IDisposable Lock();
    }
 
    public abstract class EntityFrameworkLinqRepository<TEntity> : IEntityFrameworkLinqRepository<TEntity>
        where TEntity : EntityObject
    {
        private ObjectSet<TEntity> entity;
 
        public IEntityFrameworkSessionFactory SessionFactory
        {
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
 
                Session = CreateSession(value);
                entity = Session.ObjectContext.CreateObjectSet<TEntity>();
            }
        }
 
        protected abstract IEntityFrameworkSession CreateSession(IEntityFrameworkSessionFactory sessionFactory);
 
        protected IEntityFrameworkSession Session{ get; private set; }
 
        IEntityFrameworkSession IEntityFrameworkLinqRepository<TEntity>.Session
        {
            get { return Session; }
        }
 
        ISession ILinqRepository.Session
        {
            get { return Session; }
        }
 
        protected ObjectSet<TEntity> Entity 
        {
            get
            {
                if (entity == null)
                    throw new ArgumentNullException("entity", 
                                                    "Entity is null - this is probably because you haven't set up the container WithEntityFrameworkRepository. This would result in the session not being set, thus no entity..");
 
                return entity;
            }
        }
        
        public void Add(TEntity entity)
        {
            Entity.AddObject(entity);
        }
 
        public void Update(TEntity entity)
        {
            Entity.ApplyCurrentValues(entity);
        }
 
        public void Delete(TEntity entity)
        {
            Entity.DeleteObject(entity);
        }
 
        public void Attach(TEntity entity)
        {
            Session.Attach(entity);
        }
 
        public TEntity GetAttachedItem(TEntity entity)
        {
            if (entity == null) throw new ArgumentNullException("entity");
            ObjectStateEntry stateEntry;
            if (Session.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(entity.EntityKey, out stateEntry))
            {
                return (TEntity)stateEntry.Entity;
            }
            return null;
        }
 
        public void Detach(TEntity entity)
        {
            Entity.Detach(entity);
        }
 
        public TEntity GetOrAdd(ILinqSpecification<TEntity> specification, Func<TEntity> create)
        {
            var item = FindSingle(specification);
            if (item == null)
            {
                item = create();
                Add(item);
            }
            return item;
        }
 
        public void SaveChanges()
        {
            Session.SaveChanges();
        }
 
        public TEntity FindSingle(ILinqSpecification<TEntity> specification)
        {
            return specification.SatisfyingElementsFrom(Entity).FirstOrDefault();
        }
 
        public IQueryable<TEntity> FindAll(ILinqSpecification<TEntity> specification)
        {
            return specification.SatisfyingElementsFrom(Entity);
        }
 
        public IDisposable Lock()
        {
            return Session.LockEntity(Entity.EntitySet);
        }
 
        void ILinqRepository.Add(object entity)
        {
            Add((TEntity)entity);
        }
 
        void ILinqRepository.Update(object entity)
        {
            Update((TEntity)entity);
        }
 
        void ILinqRepository.Delete(object entity)
        {
            Delete((TEntity)entity);
        }
 
        void ILinqRepository.Attach(object entity)
        {
            Attach((TEntity)entity);
        }
 
        object ILinqRepository.GetAttachedItem(object entity)
        {
            return GetAttachedItem((TEntity)entity);
        }
 
        void ILinqRepository.Detach(object entity)
        {
            Detach((TEntity)entity);
        }
    }

If you’re wondering what Lock is I’ll show that later.  That might be controversial, but it solved a problem for me…

Before we look at the implementation of session let’s have a look at a concrete repository.

public interface ISomeEntityRepository : ILinqRepository<SomeEntity>
{
    
}
 
 
public class SomeEntityRepository : TestRepositoryBase<SomeEntity>, ISomeEntityRepository
{   
}
 
public class TestRepositoryBase<T> : EntityFrameworkLinqRepository<T> 
    where T : EntityObject
{
    protected override IEntityFrameworkSession CreateSession(IEntityFrameworkSessionFactory sessionFactory)
    {
        return sessionFactory.GetSession<TestRepositoryEntitiesContainer>();
    }
}

The repository some how needs to create a ObjectContext.  You can’t (or at least I’ve not found a way) work out what object context an entity is related to.  The CreateSession method enables us to say what the context is, this is done once, so you have a base repository per object context.

The session itself is shared between repositories.  Its lifestyle is managed by the container.  This means lots of repositories can perform CRUD operations but you can ask the session to save all changes at once.  Thus, a single transaction.  This also means that objects do not need to be repeatedly fetched from the database.  Lazy loading just works in classes which have no knowledge of the repository (the behaviour is kind of like an aspect).  The thing I don’t like is having a SaveChanges method on the repository and I don’t like it because it implies that you’d just save the changes that the repository made – that’s not the case…  Maybe I’ll remove it?

Here’s the session implementation:

public class EntityFrameworkSessionFactory : IEntityFrameworkSessionFactory
{
    private readonly IServiceLocator serviceLocator;
 
    public EntityFrameworkSessionFactory(IServiceLocator serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }
 
    public IEntityFrameworkSession<TObjectContext> GetSession<TObjectContext>() where TObjectContext : ObjectContext, new()
    {
        return serviceLocator.Resolve<IEntityFrameworkSession<TObjectContext>>();
    }
 
}
 
public class EntityFrameworkSession<TObjectContext> : IEntityFrameworkSession<TObjectContext>
    where TObjectContext : ObjectContext, new()
{
    private TObjectContext context;
    private readonly List<WeakReference<EntityObject>> objectsToDetach = new List<WeakReference<EntityObject>>();
 
    public EntityFrameworkSession(IQueryCache queryCache)
    {
        if (queryCache == null) throw new ArgumentNullException("queryCache");
        Cache = queryCache;
    }
 
    public TObjectContext ObjectContext
    {
        get
        {
            if (context == null)
            {
                context = new TObjectContext();
                context.ObjectMaterialized += (o, s) => objectsToDetach.Add(new WeakReference<EntityObject>(s.Entity));
            }
            return context;
        }
    }
 
    public void Attach(EntityObject entity)
    {
        objectsToDetach.Add(new WeakReference<EntityObject>(entity));
        context.Attach(entity);
    }
 
    public void Detach(EntityObject entity)
    {
        context.Detach(entity);
    }
    
    void ISession.Attach(object entity)
    {
        Attach((EntityObject) entity);
    }
 
    void ISession.Detach(object entity)
    {
        Detach((EntityObject)entity);
    }
 
    public void SaveChanges()
    {
        foreach (var addedItem in ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
        {
            var entity = addedItem.Entity as EntityObject;
            if (entity != null)
                objectsToDetach.Add(new WeakReference<EntityObject>(entity));
        }
 
        ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
    }
 
    public void Dispose()
    {
        if (context != null)
        {
            Cache.Clear();
            DetachLoadedObjects();
            context.Dispose();
            context = null;
        }
    }
 
    private void DetachLoadedObjects()
    {
        foreach (var weakRef in objectsToDetach)
        {
            if (!weakRef.IsAlive)
                continue;
 
            var entity = weakRef.Target;
            if (entity != null && entity.EntityState != EntityState.Detached)
            {
                context.Detach(entity);
            }
        }
        objectsToDetach.Clear();
    }
 
    ObjectContext IEntityFrameworkSession.ObjectContext
    {
        get { return ObjectContext; }
    }
 
    public IQueryCache Cache
    {
        get;
        private set;
    }
 
}

The main thing to notice about the session is that when it is disposed it detaches all attached objects.  I’ll explain this later when I talk about caching, but it was to solve a problem I was noticing when load testing my WCF services…

Here’s the repository in action in a unit test:

[TestMethod]
public void Can_Query_Using_Specification()
{
    using (sessionFactory.GetSession<TestRepositoryEntitiesContainer>())
    {
        var repository = container.Resolve<ISomeEntityRepository>();
        var e = new SomeEntity {SomePropertyToLookupOn = "some Property"};
        repository.Add(e);
        repository.SaveChanges();
 
        Assert.IsNotNull(repository.FindSingle(new GetSomeEntityBySomeProperty("SOME property")));
    }
}

The specification looks like this:

public class GetSomeEntityBySomeProperty : QuerySpecification<SomeEntity>
{
    public string PropertyName { get; private set; }
 
    public GetSomeEntityBySomeProperty(string propertyName)
    {
        PropertyName = propertyName;
    }
 
 
    public override Expression<Func<SomeEntity, bool>> MatchingCriteria
    {
        get
        {
            return s => s.SomePropertyToLookupOn == PropertyName;
        }
    }
}

One thing I like here is that there is no need to mention entity framework.  The ISomeEntityRepository (it’s defined above, have a look) is not anything to do with Entity Framework and neither is the specification.  They happen to use things which derive EntityObject but that’s it. If you were refactoring to use POCO’s then the usages wouldn’t break (as long as you don’t refer to properties which only exist on the EntityObject).

Advertisements

About Tom Peplow

C# .Net developer based in London and the South Coast
This entry was posted in Uncategorized and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s