Building a Repository Pattern against an EF 5 EDMX Model - Part 1
- by Juan
I am part of a year long plus project that is re-writing an existing application for a client. We have decided to develop the project using Visual Studio 2012 and .NET 4.5. The project will be using a number of technologies and patterns to include Entity Framework 5, WCF Services, and WPF for the client UI.This is my attempt at documenting some of the successes and failures that I will be coming across in the development of the application.In building the data access layer we have to access a database that has already been designed by a dedicated dba. The dba insists on using Stored Procedures which has made the use of EF a little more difficult. He will not allow direct table access but we did manage to get him to allow us to use Views. Since EF 5 does not have good support to do Code First with Stored Procedures, my option was to create a model (EDMX) against the existing database views. I then had to go select each entity and map the Insert/Update/Delete functions to their respective stored procedure.
The next step after I had completed mapping the stored procedures to the entities in the EDMX model was to figure out how to build a generic repository that would work well with Entity Framework 5.
After reading the blog posts below, I adopted much of their code with some changes to allow for the use of Ninject for dependency injection.http://www.tcscblog.com/2012/06/22/entity-framework-generic-repository/
http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle
IRepository.cs
public interface IRepository : IDisposable where T : class
{
void Add(T entity);
void Update(T entity, int id);
T GetById(object key);
IQueryable Query(Expression> predicate);
IQueryable GetAll();
int SaveChanges();
int SaveChanges(bool validateEntities);
}
GenericRepository.cs
public abstract class GenericRepository : IRepository where T : class
{
public abstract void Add(T entity);
public abstract void Update(T entity, int id);
public abstract T GetById(object key);
public abstract IQueryable Query(Expression> predicate);
public abstract IQueryable GetAll();
public int SaveChanges()
{
return SaveChanges(true);
}
public abstract int SaveChanges(bool validateEntities);
public abstract void Dispose();
}
One of the issues I ran into was trying to do an update. I kept receiving errors so I posted a question on Stack Overflow
http://stackoverflow.com/questions/12585664/an-object-with-the-same-key-already-exists-in-the-objectstatemanager-the-object
and came up with the following hack. If someone has a better way, please let me know.
DbContextRepository.cs
public class DbContextRepository : GenericRepository
where T : class
{
protected DbContext Context;
protected DbSet DbSet;
public DbContextRepository(DbContext context)
{
if (context == null)
throw new ArgumentException("context");
Context = context;
DbSet = Context.Set();
}
public override void Add(T entity)
{
if (entity == null)
throw new ArgumentException("Cannot add a null entity.");
DbSet.Add(entity);
}
public override void Update(T entity, int id)
{
if (entity == null)
throw new ArgumentException("Cannot update a null entity.");
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
var attachedEntity = DbSet.Find(id); // Need to have access to key
if (attachedEntity != null)
{
var attachedEntry = Context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
}
}
public override T GetById(object key)
{
return DbSet.Find(key);
}
public override IQueryable Query(Expression> predicate)
{
return DbSet.Where(predicate);
}
public override IQueryable GetAll()
{
return Context.Set();
}
public override int SaveChanges(bool validateEntities)
{
Context.Configuration.ValidateOnSaveEnabled = validateEntities;
return Context.SaveChanges();
}
#region IDisposable implementation
public override void Dispose()
{
if (Context != null)
{
Context.Dispose();
GC.SuppressFinalize(this);
}
}
#endregion IDisposable implementation
}
At this point I am able to start creating individual repositories that are needed and add a Unit of Work. Stay tuned for the next installment in my path to creating a Repository Pattern against EF5.