Dane Morgridge

Programmer, Geek, ASPInsider
A blog about code and data access

  Home  |   Contact  |   Syndication    |   Login
  43 Posts | 0 Stories | 67 Comments | 0 Trackbacks

News

INETA Community Speakers Program

Twitter












Archives

Post Categories

I have moved this project from CodePlex to GitHub at https://github.com/danemorgridge/efrepo

I have a posted a project on Codeplex at http://efrepository.codeplex.com.  It is a T4 template to give you a data layer that follows Repository and Unit of Work patterns that is also ready for Dependency Injection (DI).  DI frameworks allow you to build code that is more testable and allows for a greater separation of concerns (SoC).  This is not the only use for them, but it is a big one and what they are commonly popular for.  You don’t have to use DI to get a good separation of concerns, but it certainly can help.  It is one of the most important things that developers can do to make their code more maintainable but is unfortunately one of the things that is commonly overlooked.

Most applications these days work with data at some point and a large amount of them use relational databases.  Many developers use Object Relational Mapping (ORM) tools to make that process easier. Entity Framework is Microsoft’s ORM and data access strategy moving forward.  LINQ to SQL also exists but Entity Framework is where the new innovation will happen.  As such, it is easy to use the ORM tool as your data access layer and not just a data access technology.  While this works, it doesn’t lend it self to an application with a good separation of concerns. 

When I build an application, I normally try to separate the code out by assemblies into logical pieces.  If you don’t separate by assemblies, at they very least separate by namespaces.  This will give you the ability to separate by assemblies later if you need to.  One common thing I like to separate is the data access layer.  There are several reasons for this, but one of the biggest is simply for code reuse.  I rarely write an application that doesn’t have multiple pieces that need to access data so having it separated reduces duplication and thus lends to a more maintainable and sustainable application.

So what does this have to do with my T4 template project on CodePlex.  This template will generate files necessary to give you a persistent ignorant, testable data layer without having to write a great deal of code.  I am huge fan of code gereration where it makes sense and this is one area where I think it does.  I also feel that you should know what code generation is doing.  This makes debugging much easier.  I am going to focus on this post on what files are generated and how to use what is there to build your data layer.  I will have additional samples available in the future with different DI frameworks and more advanced samples. 

The template looks for an .edmx file in the same directory with it and will generate several files based on your model.  Most of these files will be re-generated when the T4 template is executed but there are few that will not.  These will allow you to put custom logic in those files without having to create your own partial classes.

IRepository.cs
IUnitOfWork.cs
EFRepository.cs
EFUnitOfWork.cs
RepositoryIQueryableExtensions.cs
{EntityName}Repository.cs
{EntityName}Repository.Generated.cs

IRepository.cs : Interface for the Repository portion.  This is one of two interfaces that are generated.  It is structured so that when mocking for testing, you only need to mock the two interfaces and everything wil fal into place properly.

   1: public interface IRepository<T>
   2: {
   3:     IUnitOfWork UnitOfWork { get; set; }
   4:     IQueryable<T> All();
   5:     IQueryable<T> Find(Func<T, bool> expression);
   6:     void Add(T entity);
   7:     void Delete(T entity);
   8:     void Save();
   9: }

You can see there is a property for an IUnitOfWork which will hold the actual context.  Basic CRUD functions are exposed here as well.  This will likely be expanded as the project grows and matures.  Feel free to give me feedback on anything you would like to see added here.

IUnitOfWork.cs : Interface for the Unit of Work


   1: public interface IUnitOfWork
   2: {
   3:     ObjectContext Context { get; set; }
   4:     void Save();    
   5:     bool LazyLoadingEnabled { get; set; }
   6:     bool ProxyCreationEnabled { get; set; }
   7:     string ConnectionString { get; set; }
   8: }

The ObjectContext in the UnitOfWork is the only dependency on Entity Framework in the project and is set to the real concrete ObjectContext in the EFUnitOfWork.  The other properties are used to set properties on the object context without having to directly access the context itself.  Since this interface is the gateway into the Entity Framework, this is the one that you will want to mock when building tests.

EFRepository.cs : Concrete class for actually working with Entity Framework classes

   1: public class EFRepository<T> : IRepository<T> where T : class
   2: {
   3:     public IUnitOfWork UnitOfWork { get; set; }
   4:     private IObjectSet<T> _objectset;
   5:     private IObjectSet<T> ObjectSet
   6:     {
   7:         get
   8:         {
   9:             if (_objectset == null)
  10:             {    
  11:                 _objectset = UnitOfWork.Context.CreateObjectSet<T>();
  12:             }
  13:             return _objectset;
  14:         }
  15:     }
  16:     
  17:     public virtual IQueryable<T> All()
  18:     {
  19:         return ObjectSet.AsQueryable();
  20:     }
  21:     
  22:     public IQueryable<T> Find(Func<T, bool> expression)
  23:     {
  24:         return ObjectSet.Where(expression).AsQueryable();
  25:     }
  26:     
  27:     public void Add(T entity)
  28:     {
  29:         ObjectSet.AddObject(entity);
  30:     }
  31:     
  32:     public void Delete(T entity)
  33:     {
  34:         ObjectSet.DeleteObject(entity);
  35:     }
  36:     
  37:     public void Save()
  38:     {
  39:         UnitOfWork.Save();
  40:     }
  41: }

There is a lot going on here but the key thing to note in this file is the IObjectSet<T>.  This is how the ObjectContext contained inside the Unit of Work knows which classes to work with.  This class is the basis for all of our actual data layer classes as you will see shortly.  Using a combination of the methods exposed here you should be able to do anything you need to do to query and work with entities.

EFUnitOfWork.cs : Mostly properties that interface with the context.

   1: public partial class EFUnitOfWork : IUnitOfWork
   2: {
   3:     public ObjectContext Context { get; set; }
   4:     
   5:     public EFUnitOfWork()
   6:     {
   7:         Context = new DataModelContainer();
   8:     }
   9:     
  10:     public void Save()
  11:     {
  12:         Context.SaveChanges();
  13:     }
  14:     
  15:     ...
  16:    }

In the constructor the actual concrete ObjectContext is set.  This is added directly by the T4 template and is extracted directly from the .emdx file. The ObjectContext is exposed as a public property so you can access it directly if need be. 

There are two more classes that get gererated for each entity.  They are {Entity}Repository.cs and {Entity)Repository.gererated.cs.  The {Entity}Repository.generated.cs file contains the code that interfaces with the IRepository<T> and IUnitOfWork.  The {Entity}Repository.cs classes are where you will set your own custom data layer logic.  This is where this template will really help.  If we have a Person entity we will have PersonRepository.cs and a PersonRepository.generated.cs. 

PersonRepository.gererated.cs : The glue that makes it all work

   1: public partial class PersonRepository
   2: {
   3:     private IRepository<Person> _repository {get;set;}
   4:     public IRepository<Person> Repository
   5:     {
   6:         get { return _repository; }
   7:         set { _repository = value; }
   8:     }
   9:     
  10:     public PersonRepository(IRepository<Person> repository, IUnitOfWork unitOfWork)
  11:     {
  12:         Repository = repository;
  13:         Repository.UnitOfWork = unitOfWork;
  14:     }
  15:     
  16:     public IQueryable<Person> All()
  17:     {
  18:         return Repository.All();
  19:     }
  20:  
  21:     public void Add(Person entity)
  22:     {
  23:         Repository.Add(entity);
  24:     }
  25:     
  26:     public void Delete(Person entity)
  27:     {
  28:         Repository.Delete(entity);
  29:     }
  30:  
  31:     public void Save()
  32:     {
  33:         Repository.Save();
  34:     }
  35:  
  36:     
  37: }

The first thing you will likely notice is that the class doesn’t inherit from anything. This is by design as I would still have to implement all of the methods if I inherited directly from IRepository<Person>.  I have some plans to make a base class that will reduce the total amount of code, I’m just not finished with it yet.  Be looking for that in the coming weeks.  The PersonRepository class now has access to all of the basic methods from IRepository<T> because they are re-implemented here.  The Repository property is what gives you the access to this. 

We have here basic CRUD operations to Add (Create) Read (Find) Update (Save) and Delete (Delete).  All are very self explainitory with maybe the exception of Find.  The Find method takes a lambda expression just like the Where clause of an IQueryable.  In fact, it passes it directly to the Where clause of an IQueryable.  While the methods in this class are great, they are not really meant to be used directly but rather through data access methods.  You certainly can use them directly, but I would recommend another approach.

The PersonRepository.cs file won’t get overwritten when the template runs so that is where you would put your logic.  Let’s start by creating a method to find a person by their primary key.  We will simply call it GetPerson(int personId).  We will then call Repository.Find() and query for the personId:

   1: public Person GetPerson (int personId)
   2: {
   3:     return Repository.Find(p => p.PersonId == personId).FirstOrDefault();
   4: }

We use the Repository property that is in the generated file to gain access to the Entity Framework.  We are doing a basic query here to find the person based on their associated personId.  we are calling FirstOrDefault() to return teh first entity that is in the result set.  If there are none, it will return null.  We could just use First(), but it would through an exception and I would rather do a null check personally.  Now that we have a method to use, let’s use it.

The first thing you need to do to create PersonRepository instance so you can access the newly created method:  The PersonRepository has a constructor that does takes in a couple of properties:

   1: public PersonRepository(IRepository<Person> repository, IUnitOfWork unitOfWork)

It looks for an IRepository<Person> and an IUnitOfWork.  These can be satisfied by using an EFRepository<Person> and an EFUnitOfWork. To create our instance we will use the following:

   1: var personRepository = new PersonRepository(new EFRepository<Person>(), new EFUnitOfWork());

We can now call execute our method:

   1: int personId = 1;
   2: var person = personRepository.GetPerson(personId);

If person comes back null, then we know it wasn’t found.  Otherwise, we have our entity to start working with.  It’s as easy as that.

Now if you notice, I created a new EFUnitOfWork.  This is fine if you are only using one repository class. If you need to actively use more than one, you will need to create a separate unit of work and pass it in.  We also have an AddressRepository so it would look like this:

   1: var unitOfWork = new EFUnitOfWork();
   2: var personRepository = new PersonRepository(new EFRepository<Person>(), unitOfWork);
   3: var addressRepository = new AddressRepository(new EFRepository<Address>(), unitOfWork);
   4:  
   5: // Do some stuff requiring saving
   6:  
   7: unitOfWork.Save();

Here is where the unit of work pattern comes into play. You can perform multiple action on the unit of work within multiple repositories and then call Save at once.  the Save() call inturn calls the SaveChanges() method on the ObjectContext which will by default wrap the entire unit of work in a transaction.  If anything fails, the whole operation is rolled back.

Now these methods aren’t as easily testable as they could be.  In the next post we will look at how it works with Dependency Injection and specifically StructureMap. StructureMap makes the methods more testable and at the same time make the whole process easier without adding unecessary complexity.

Go to efrepository.codeplex.com to download!

posted on Monday, June 28, 2010 3:02 PM