Entity Framework 4.0 Beta 1 – POCO, ObjectSet, Repository and UnitOfWork

In the old Entity Framework, creating a generic repository wasn’t impossible, but not really elegant either. Any way you looked at it, you needed to deal with strings to get it working.

Lets say you want to create an Add method on your repository, there was no way getting around something like this:

    6 

    7         objectContext.AddObject("OrderSet", newOrder)

    8 

So for each (base) entity you have in your conceptual model you have a corresponding EntitySet, and in EF1 you needed to pass in the string name of the entityset to add the object; the name is not necessarily the same as the name of the entity.

In a generic repository you would have to figure out the name of the current entityset – you can figure it out with a bit of reflection on the concrete ObjectContext, but its not pretty, errorprone and it took me a while to get there.

Enter ObjectSet<T>

Fortunately in EF4 the strongly typed properties on the ObjectContext for each EntitySet return ObjectSet<T> instead of ObjectQuery<T>. And since the ObjectContext also has a method called CreateObjectSet<T> you can instead do something like this:

    1 

    2 private IObjectSet<T> ObjectSet

    3 {

    4     get

    5     {

    6         if (_objectSet == null)

    7         {

    8             _objectSet = this.Context.CreateObjectSet<T>();

    9         }

   10         return _objectSet;

   11     }

   12 }

   13 

   14 public void Add(T entity)

   15 {

   16     this.ObjectSet.AddObject(entity);

   17 }

   18 

Notice the IObjectSet<T>  which is very handy as it enables you to fake the ObjectSet for testing for instance. Now to be fair, this is how it should have been in EF1, but on an optimistic note im happy its changed.

Repository<T>

In order to have the right context for testing EF4 properly with persistance ignorance and POCO we will set up a Repository and UnitOfWork pattern for the OrderModel project i did in my previous EF4 post. Im not going to cover every detail of this, but I will run through the setup, and then you can get the code and have a closer look on your own.

First i set up the repository contract and create an IRepository<T> where T is a POCO class.

    1 

    2 public interface IRepository<T> where T : class

    3 {

    4     IQueryable<T> GetQuery();

    5 

    6     IEnumerable<T> GetAll();

    7     IEnumerable<T> Find(Func<T, bool> where);

    8     T Single(Func<T, bool> where);

    9     T First(Func<T, bool> where);

   10 

   11     void Delete(T entity);

   12     void Add(T entity);

   13     void Attach(T entity);

   14     void SaveChanges();

   15 }

   16 

Next we will define a generic repository for the Entity Framework that implements IRepository<T>:

    1 

    2 public class EFRepository<T> : IRepository<T> where T : class

    3 {

    4     ObjectContext _context;

    5     IObjectSet<T> _objectSet;

    6 

    7     private ObjectContext Context

    8     {

    9         get

   10         {

   11             if (_context == null)

   12             {

   13                 _context = GetCurrentUnitOfWork

   14                     <EFUnitOfWork>().Context;

   15             }

   16             return _context;

   17         }

   18     }

   19 

   20     private IObjectSet<T> ObjectSet

   21     {

   22         get

   23         {

   24             if (_objectSet == null)

   25             {

   26                 _objectSet = this.Context

   27                     .CreateObjectSet<T>();

   28             }

   29             return _objectSet;

   30         }

   31     }

   32 

   33     public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>()

   34         where TUnitOfWork : IUnitOfWork

   35     {

   36         return (TUnitOfWork)UnitOfWork.Current;

   37     }

   38 

   39     public IQueryable<T> GetQuery()

   40     {

   41         return ObjectSet;

   42     }

   43 

   44     public IEnumerable<T> GetAll()

   45     {

   46         return GetQuery().ToList();

   47     }

   48 

   49     public IEnumerable<T> Find(Func<T, bool> where)

   50     {

   51         return this.ObjectSet.Where<T>(where);

   52     }

   53 

   54     public T Single(Func<T, bool> where)

   55     {

   56         return this.ObjectSet.Single<T>(where);

   57     }

   58 

   59     public T First(Func<T, bool> where)

   60     {

   61         return this.ObjectSet.First<T>(where);

   62     }

   63 

   64     public void Delete(T entity)

   65     {

   66         this.ObjectSet.DeleteObject(entity);

   67     }

   68 

   69     public void Add(T entity)

   70     {

   71         this.ObjectSet.AddObject(entity);

   72     }

   73 

   74     public void Attach(T entity)

   75     {

   76         this.ObjectSet.Attach(entity);

   77     }

   78 

   79     public void SaveChanges()

   80     {

   81         this.Context.SaveChanges();

   82     }

   83 }

   84 

Now thats pretty straight forward. The ObjectContext actually already works like a UnitOfWork, but in order to handle the lifespan of our ObjectContext we wrap it up in our own UnitOfWork implementation.

    1 

    2 public class EFUnitOfWork : IUnitOfWork, IDisposable

    3 {

    4     public ObjectContext Context { get; private set; }

    5 

    6     public EFUnitOfWork(ObjectContext context)

    7     {

    8         Context = context;

    9     }

   10 

   11     public void Commit()

   12     {

   13         Context.SaveChanges();

   14     }

   15 

   16     public void Dispose()

   17     {

   18         if (Context != null)

   19         {

   20             Context.Dispose();

   21         }

   22         GC.SuppressFinalize(this);

   23     }

   24 }

   25 

IUnitOfWork consists of the method  Commit, and in our concrete implementation we save changes on our current ObjectContext on commmit.

To handle units of work we implement a static class that handles both the case of a webcontext and a multithreaded non web context. The ObjectContext is not threadsafe, so we need to take that in consideration.

The interesting bit of that class is the Current property that gets the current UnitOfWork:

    1 

    2 public static IUnitOfWork Current

    3 {

    4     get

    5     {

    6         IUnitOfWork unitOfWork = GetUnitOfWork();

    7         if (unitOfWork == null)

    8         {

    9             _unitOfWorkFactory = ObjectFactory.

   10                         GetInstance<IUnitOfWorkFactory>();

   11             unitOfWork = _unitOfWorkFactory.Create();

   12             SaveUnitOfWork(unitOfWork);

   13         }

   14         return unitOfWork;

   15     }

   16 }

   17 

We are going to look at the two methods that do the work in a moment, but first of all notice that we have a UnitOfWorkFactory to produce our specific UnitOfWork. Furthermore we are going to use the StructureMap Service Locator to get our dependency on a concrete factory.

Either in configuration or in code we need to set up which concrete type the requested type should resolve to like so. In code it would be something like this:

    2 

    3 ObjectFactory.Initialize(

    4     x =>

    5     {

    6         x.ForRequestedType<IUnitOfWorkFactory>().

    7             TheDefaultIsConcreteType<EFUnitOfWorkFactory>();

    8 

    9         x.ForRequestedType(typeof(IRepository<>)).

   10             TheDefaultIsConcreteType(typeof(EFRepository<>));

   11     }

   12 );

   13 

We map it to the concrete factory implementation for our Entity Framework project. The IRepository<T> is also mapped to the concrete type EFRepository that we created earlier.

The EFUnitOfWork factory has the responsibility of creating new units of work, implements the IUnitOfWorkFactory and has a method that takes a delegate which, when called creates an instance of the ObjectContext we want to work on.

    1 

    2 public class EFUnitOfWorkFactory : IUnitOfWorkFactory

    3 {

    4     private static Func<ObjectContext> _objectContextDelegate;

    5     private static readonly Object _lockObject = new object();

    6 

    7     public static void SetObjectContext

    8         (Func<ObjectContext> objectContextDelegate)

    9     {

   10         _objectContextDelegate = objectContextDelegate;

   11     }

   12 

   13     public IUnitOfWork Create()

   14     {

   15         lock (_lockObject)

   16         {

   17             ObjectContext context = _objectContextDelegate();

   18         }

   19         return new EFUnitOfWork(context);

   20     }

   21 }

   22 

UnitOfWork/ObjectContext lifespan

So back to the UnitOfWork. As mentioned the ObjectContext is not threadsafe, so we need to make sure that when re-using the objectcontext we dont run into issues with that.

If we are in a web-application we probably want to at least scope the context to maximum the lenght of the request. If we tied it to a static class member, it would share lifespan with the AppDomain and that is bound to cause problems.

Maybe it would make sense to work in smaller cohesive chunks of one or few business transactions and you could create a UnitOfWork scope to handle that, but for the sake of the example we are going to tie it to HttpContext.Current.Items and thereby limit the lifespan to the current request.

When we are not in a web application we need to handle multithreaded scenarios at least, and it would probably make most sense to have a scope of a business transaction, but in this context ill just handle the threading issue. This leaves us with the following methods on UnitOfWork where _threads is a Hashtable:

    1 

    2 private static IUnitOfWork GetUnitOfWork()

    3 {

    4     if (HttpContext.Current != null)

    5     {

    6         if (HttpContext.Current.Items.

    7                     Contains(HTTPCONTEXTKEY))

    8         {

    9             return (IUnitOfWork)HttpContext.Current.

   10                                     Items[HTTPCONTEXTKEY];

   11         }

   12         return null;

   13     }

   14     else

   15     {

   16         Thread thread = Thread.CurrentThread;

   17         if (string.IsNullOrEmpty(thread.Name))

   18         {

   19             thread.Name = Guid.NewGuid().ToString();

   20             return null;

   21         }

   22         else

   23         {

   24             lock (_threads.SyncRoot)

   25             {

   26                 return (IUnitOfWork)_threads

   27                     [Thread.CurrentThread.Name];

   28             }

   29         }

   30     }

   31 }

   32 

   33 private static void SaveUnitOfWork(IUnitOfWork unitOfWork)

   34 {

   35     if (HttpContext.Current != null)

   36     {

   37         HttpContext.Current.Items

   38                     [HTTPCONTEXTKEY] = unitOfWork;

   39     }

   40     else

   41     {

   42         lock(_threads.SyncRoot)

   43         {

   44             _threads[Thread.CurrentThread.Name] = unitOfWork;

   45         }

   46     }

   47 }

   48 

Conclusion

So we end up being able to write the following code:

    5 IRepository<Order> orderRep = ObjectFactory.

    6     GetInstance<IRepository<Order>>();

    7 IRepository<Customer> custRep = ObjectFactory.

    8     GetInstance<IRepository<Customer>>();

    9 

   10 Order o = new Order();

   11 o.CreatedDate = DateTime.Now;

   12 o.Status = 100;

   13 o.Customer = custRep.First(x => x.Id == 1);

   14 orderRep.Add(o);

We are programming against the interfaces and we use our POCO classes. We hook up our concrete DB dependend implementations with the StructureMap Service Locator, and we have control over the scope and lifespan with our UnitOfWork implementation.

But have a look at the code. Be advised though - it is not production ready code – it needs tests, guards and errorhandling, and it serves only as a sort of Proof of concept for EF4.

Friday, June 12, 2009 5:35:13 PM (Romance Standard Time, UTC+01:00)
Nice article Joachim.

I'd love to have a chat sometime and get your thoughts on how EF 4 can be improved.

- Alex
Saturday, June 13, 2009 11:00:45 PM (Romance Standard Time, UTC+01:00)
Thanks.

Sure, I would like that. Just write me an email using the contact link above, and we can set something up.
Saturday, June 20, 2009 1:11:50 AM (Romance Standard Time, UTC+01:00)
Hi
A question:

If I have two or more changes pending, I need use "begin transaction" before call Context.SaveChanges() or it is not necesary ?
Marcelo
Sunday, June 28, 2009 8:38:30 PM (Romance Standard Time, UTC+01:00)
Great article! Thanks.
Tuesday, July 07, 2009 6:03:53 PM (Romance Standard Time, UTC+01:00)
I have a question for you:
why is there a need for EFUnitOfWorkFactory.SetObjectContext(() => new OrderModelContainer());
Shouldn't it be up to the EFUnitOfWorkFactory which type of ObjectContext it instantiates?
John Allocate
Tuesday, July 07, 2009 7:17:45 PM (Romance Standard Time, UTC+01:00)
Hi John.

Well, the thought was that the EfUnifOfWorkFactory is a generel factory for EF, and so I didn't want it to be tied to a concrete EF model / ObjectContext. This way you can inject any ObjectContext.

You might inject one for testing purposes, or simply want to use the Data library from different projects, with different models within the same solution. If you use the EFProviderWrapper to wrap your ObjectContext, you could easily inject that instead of the original one when needed.

You could argue of course, that it would make more sense to resolve the concrete ObjectContext with the IOC container and do this instead:

x.ForRequestedType<ObjectContext>()
.TheDefaultIsConcreteType<OrderModelContainer>();

[...]

context = ObjectFactory.GetInstance<ObjectContext>();

Friday, July 10, 2009 4:46:41 AM (Romance Standard Time, UTC+01:00)
Hi Joachim

where this answer to my question ?
Marcelo
Monday, July 13, 2009 3:32:26 PM (Romance Standard Time, UTC+01:00)
Thank you Joachim, that makes more sense.
I appreciate you taking the time. Great blog by the way, nicely done.
John Allocate
All comments require the approval of the site owner before being displayed.
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview