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<T> Query(Expression<Func<T,bool>> predicate);
IQueryable<T> 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<T> Query(Expression<Func<T,bool>> predicate);
public abstract IQueryable<T> 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<T> DbSet;
public DbContextRepository(DbContext context)
{
if (context == null)
throw new ArgumentException("context");
Context = context;
DbSet = Context.Set<T>();
}
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<T>();
}
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.