I’ve been working on some side applications and really wanted to build a generic Repository class in order to funnel all of my data access through. The Repository pattern is explained very well by Ayende here: http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/10/08/the-repository-pattern.aspx
At first, I created a separate Repository class for each and every entity that existed. Each of these repositories had all of the basic CRUD operations, as well as any custom ones. This led to many classes.
So if you had a Product, Customer and Address table in your database, that means you have a Product, Customer and Address Entity for each, then I would have a ProductRepository, CustomerRepository and AddressRepository class for each. I wanted a more terse way to do this with less code.
After reading much of Entity Framework in Action (http://www.manning.com/mostarda/), there was a good pattern for a repository base, so I took that and expanded it for my purposes.
I created two intefaces IRepository and IUnitOfWork:
public interface IRepository<T> : ICollection<T> where T : class
{
IEnumerable<T> Query(Func<T, bool> predicate);
T GetSingle(Func<T, bool> predicate);
void Update(T Entity);
List<T> GetAll();
}
IUnitOfWork
public interface IUnitOfWork<T> where T : class
{
void SaveChanges();
}
And here is my Repository class
public class Repository<T> : IRepository<T>, IUnitOfWork<T> where T : class
{
private ObjectContext context;
private ObjectSet<T> entitySet;
private bool sharedContext = false; // this just means it was not passed in
public Repository(bool ReadOnly)
{
ConstructorLoad(new SampleEntities(), ReadOnly); // default the entitites, but allow for readonly override
}
public Repository()
{
ConstructorLoad(new SampleEntities(), true); // default to read only entities
}
public Repository(ObjectContext context)
{
sharedContext = true;
ConstructorLoad(context, true); // default to read only entities
}
public Repository(ObjectContext context, bool ReadOnly)
{
sharedContext = true;
ConstructorLoad(context, ReadOnly);
}
private void ConstructorLoad(ObjectContext context, bool ReadOnly)
{
if (context == null)
{
sharedContext = false;
context = new SampleEntities();
}
this.context = context;
this.entitySet = context.CreateObjectSet<T>();
if (ReadOnly)
this.entitySet.MergeOption = MergeOption.NoTracking; // this will help add a lot of performance to calls, most are reads, no need for tracking
}
public IEnumerable<T> Query(Func<T, bool> predicate)
{
return this.entitySet.Where(predicate);
}
public List<T> GetAll()
{
return this.entitySet.ToList<T>();
}
public T GetSingle(Func<T, bool> predicate)
{
return this.Query(predicate).Single();
}
public void SaveChanges()
{
context.SaveChanges();
}
public void Add(T entity)
{
if (!this.Contains(entity))
this.entitySet.AddObject(entity);
}
public void Update(T entity)
{
if (!this.Contains(entity))
this.entitySet.Attach(entity);
}
public bool Remove(T entity)
{
if (!this.Contains(entity))
return false;
this.entitySet.DeleteObject(entity);
return true;
}
public bool Contains(T item)
{
ObjectStateEntry state;
if (!this.context.ObjectStateManager.TryGetObjectStateEntry(item, out state))
return false;
return (state.State != EntityState.Detached);
}
public void Clear()
{
}
public void CopyTo(T[] array, int arrayIndex)
{
}
public int Count
{
get
{
return 0;
}
}
public bool IsReadOnly
{
get { return false; }
}
public IEnumerator<T> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
~Repository()
{
if (!sharedContext && context != null)
{
context.Dispose();
context = null;
}
}
So now we have all of our generic CRUD items encapsulated within a generic Repository class. In theory, I could switch my database and my ORM over to something else such as nHibernate, and would have very little impact to my codebase that uses this repository.
To use this repository to get one record by ID, I would call this:
Repository<Product> productRepo = new Repository<Product>();
Product product = productRepo.GetSingle(x => x.ProductId = 1);
I could also use it to get a list of all Products
List<Product> products = new Repository<Product>().GetAll();
Or I could save a new Product
Repository<Product> productRepo = new Repository<Product>(false);
Product product = new Product{
ItemName = “New Product”,
Price = 32.99,
Available = true
};
productRepo.Add(product);
productRepo.SaveChanges(product);
And finally to delete
Repository<Product> productRepo = new Repository<Product>(false);
productRepo.Remove(product);
productRepo.SaveChanges();
A couple of notes about the Repository
1. I’ve defaulted that the MergeOption is set to NoTracking by default, since the vast majority of my calls are reads, so I don’t need the tracking on. This helps with performance, but can be a bit tricky if you forget to pass in a true variable to turn it on when you need to work with entities and save them.
if (ReadOnly)
this.entitySet.MergeOption = MergeOption.NoTracking; // this will help add a lot of performance to calls, most are reads, no need for tracking
2. ObjectContext is an expensive operation. You can pass in the ObjectContext via the constructor or let it build one for you if just doing a single call. It will Dispose of the context when the class terminates.
~Repository()
{
if (!sharedContext && context != null)
{
context.Dispose();
context = null;
}
}
Then I create a Service account for many of my related functionality and call the Repository from there. For example the ProductService. The services are not necessarily a one to one to the entities. Less services.
I’m curious to see what everyone else is using for their Repositories. I wasn’t able to find a ton of information on it out there on the Intertubes.
Cheers!