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.