Aspen – A sample app using Silverlight 4 and .Net 4.0 – part 3 of X – Repository implementation
Note: The example code in this blog post is based on the VS2010 Beta 2, changes can be made until VS2010 hits RTM.
In my
previous post
I wrote about how I use MEF and Entity Framework 4.0
Code-only feature. I have done a lot of refactoring and
added the first Repository to the “Aspen” application since
then. In this post I’m going to show you the refactoring I
have made and how I have implemented the Repository for the
Member Entity, MemberRepository.
Changes made since the previous post
I made some changes to the AspenObjectContextFactory.
I made it more generic and renamed the class to
ObjectContextFactory, the class now implements an interface,
IObjectContextFactory:
public interface IObjectContextFactory { ObjectContext Create(DbConnection con); ObjectContext Create(string connectionString); }
The following code is the ObjectContextFactory:
public class ObjectContextFactory : IObjectContextFactory { [ImportMany(typeof(StructuralTypeConfiguration), AllowRecomposition = true)] private List<StructuralTypeConfiguration> _configurations = null; public ObjectContext Create(string connectionString) { if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException("connectionString"); return this.Create(new SqlConnection(connectionString)); } public ObjectContext Create(DbConnection con) { if (con == null) throw new ArgumentNullException("con"); if (_configurations == null) this.Compose(); var builder = new ContextBuilder<ObjectContext>(); if (_configurations != null) _configurations.ForEach(c => builder.Configurations.Add(c)); return builder.Create(con); } private void Compose() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var catalogParts = new AggregateCatalog(); foreach (var assembly in assemblies) { if (assembly.FullName.Contains("EntityMapping")) catalogParts.Catalogs.Add( new AssemblyCatalog(assembly)); } var container = new CompositionContainer(catalogParts); container.ComposeParts(this); } }
The changes made to the “Aspen”ObjectContextFactory
are a new constructor, taking a connection string as an
argument and will create a SqlConnection by default. The
other changes made to the class is to make sure it will only
return an ObjectContext, not the AspenObjectContext as
before (AspenObjectContext is now removed from the project).
Repository
When the ObjectContextFactory was in place I wanted to
use it, so I created the first Repository, MemberRepository.
The MemberRepository will take an IObjectContext as an
argument. The reason why I want the Repository to take an
IObjectContext is to make sure several repositories in the
future can share the same ObjectContext and also remove some
dependencies etc (Using the
Dependency Inversion Principle). Entity Framework doesn’t have an IObjectContext
interface and I will not use the ObjectContext because it
will only make my Repository to be dependent on a detail
instead of an abstraction (Will make it harder for me to
write unit test). So I had to create my own IObjectContext
interface:
public interface IObjectContext : IDisposable { IObjectSet<T> CreateObjectSet<T>() where T : class; void SaveChanges();
}
The IObjectContext interface has two methods, the
CreateObjectSet<T> and the SaveChanges. At the moment
I don’t need more methods. I created an ObjectContextAdapter
which inherits the IObjectContext interface, the
ObjectContextAdapter will use the ObjectContextFactory to
create an instance of an ObjectContext. Here is the
ObjectContextAdapter:
public class ObjectContextAdapter : IObjectContext { private ObjectContext _context = null; private IObjectContextFactory _objectContextFactory = new ObjectContextFactory(); public ObjectContextAdapter(string connectionString) { _context = _objectContextFactory.Create(connectionString); } public ObjectContextAdapter(DbConnection con) { _context = _objectContextFactory.Create(con); } public ObjectContextAdapter(ObjectContext context) { _context = context; } public IObjectSet<T> CreateObjectSet<T>() where T : class { return _context.CreateObjectSet<T>(); } public void SaveChanges() { _context.SaveChanges(); } public void Dispose() { _context.Dispose(); } }
First I was thinking about making it possible to
replace the IObjectContextFactory, but didn’t really find
any reason to do so. I will see if I will made it possible
in the future. The following is an example how the
ObjectContextAdapter will be used in the production code:
var objectContext = new ObjectContextAdapter( ConfigurationManager.ConnectionStrings["AspenConnectionString"].ConnectionString); var memberRepository = new MemberRepository(objectContext);
Because the ObjectContextAdapter will create the
instance of an ObjectContext, I can now for example use
Unity or StructureMap etc to create an instance of an
Repository and use dependency injection. Here is the how I
have implemented the MemberRepository:
public class MemberRepository : IMemberRepository { readonly IObjectContext _objectContext = null; readonly IObjectSet<Member> _objectSet = null; public MemberRepository(IObjectContext objectContext) { if (objectContext == null) throw new ArgumentNullException("objectContext"); _objectContext = objectContext; _objectSet = _objectContext.CreateObjectSet<Member>(); } public IQueryable<Member> GetQuery() { return _objectSet; } public IEnumerable<Member> GetAll() { return _objectSet.ToList(); } public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> where) { return _objectSet.Where(where); } public TEntity Single(Expression<Func<TEntity, bool>> where) { return _objectSet.SingleOrDefault(where); }
public void Delete(Member entity) { _objectSet.DeleteObject(entity); this.SaveChanges(); } public void Add(Member entity) { _objectSet.AddObject(entity); this.SaveChanges(); } private void SaveChanges() { _objectContext.SaveChanges(); } }
Note: I could have done a more generic Repository
without creating one for each Entity, but I decided to
not do that. I prefer to have a Repository for each
Entity instead of an generic one.
The IMemberRepository interface implemented by the
MemberRepository is kind of empty right now, it implements
an generic IRepository<T> interface:
public interface IMemberRepository : IRepository<Member> { }
The following is the IRepository<T> code:
public interface IRepository<T> where T : class { IQueryable<T> GetQuery(); IEnumerable<T> GetAll(); IEnumerable<T> Find(Expression<Func<TEntity, bool>> where); T Single(Expression<Func<TEntity, bool>> where); void Delete(T entity); void Add(T entity); }
If you have any comments about anything, please let me
know.
Testing
I will end this blog post about mentioning something about
testing. Because I have created the IObjectContext
interface, I don’t need to create a Stub for my
MemberRepository in my unit tests, instead I can mock the
IObjectContext. Here are the classes I use in my unit tests
for the moment:
The ObjectContext which will be
passed to the MemberRepository:
class MockObjectContext : IObjectContext { public IObjectSet<T> CreateObjectSet<T>() where T : class { return new MockObjectSet<T>(); } public void SaveChanges() { } public void Dispose() { } }
The ObjectSet for testing, MockObjectSet<T>:
class MockObjectSet<T> : IObjectSet<T> where T : class { private List<T> _items = new List<T>(); public void AddObject(T entity) { _items.Add(entity); } public void Attach(T entity) { } public void DeleteObject(T entity) { _items.Remove(entity); } public void Detach(T entity) { }
public IEnumerator<T> GetEnumerator() { return _items.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _items.GetEnumerator(); } public Type ElementType { get { return typeof(T); } } public System.Linq.Expressions.Expression Expression { get { return null; } } public IQueryProvider Provider { get { return null; } } }
Here is how I can test my MemberRepository:
var mockObjectContext = new MockObjectContext(); var memberRepository = new MemberRepository(mockObjectContext); memberRepository.Add(new Member() { FirstName = "John", LastName = "Doe" }); Assert.AreEqual<int>(1, memberRepository.GetAll().Count());
This test is kind of dumb, but I decided to use some sort of TDD (Test Driven Design) first to make sure I find a good design for the Repository etc.
If you want to know when I publish a new blog post to my blog, you can follow me on twitter: http://www.twitter.com/fredrikn