We are starting to develop a small framework in our company, to share code between different applications. For data access we are using EF4. We have a custom DbContext class and a generic Repository:
public class RMDbContext : DbContext
{
// ....
}
public interface IRepository
{
IQueryable<T> All();
void Delete(T entity) where T : class;
void Add(T entity) where T : class;
void Update(T entity) where T : class;
int SaveChanges();
void RollbackChanges();
}
The problem here is how to implement the repository, using our custom DbContext class (RMDbContext). My co-worker thinks that the best way is to let RMDbContext implement the IRepository interface:
public class RMDbContext : DbContext, IRepository
{
// ....
}
To be honest I don't like this approach, because the context is tied to a specific contract (IRepository). IMO it's better to create a repository implementation that uses the RMDbContext, something like this:
public class Repository<T> : IRepository where T : RMDbContext, new()
{
protected readonly RMDbContext context;
public class Repository()
{
context = new T();
}
// ....
}
What do you think about these 2 approaches? Which one would you choose, and why?
What we did at work was to implement a pattern like this:
interface ICRUD<T> : ICreatable<T>, IRetrievable<T>, IUpdatable<T>, IDeletable<T>
{
}
interface ICreatable<T>
{
T Create();
}
interface IRetrieve<T>
{
T Retrieve(params object[] keys);
}
interface IUpdatable<T>
{
void Update(T existing);
}
interface ICreatable<T>
{
void Delete(T existing);
}
And then we created an Entity-powered base repository:
public abstract class BaseRepository<TModel, TEntities> where TEntities : IDbSet<TModel>
{
protected TEntities Entities {get; set;}
protected DbContext Db {get; set;}
public BaseRepository (DbContext db, TEntities entities)
{
Db = db;
Entities = entities;
}
public virtual TModel Create() { return Entities.Create (); }
public virtual TModel Retrieve (params object[] keys) { return Entities.Find (keys); }
public virtual void Update (TModel existing) { Db.Entry(existing).State = Modified; }
public virtual void Delete (TModel existing) { Db.Entry(existing).State = Removed; }
}
If you notice, the BaseRepository doesn't actually use ICRUD, just has identical method signatures. Since we code to interfaces, this lets us use a lot of shared code without exposing functionality we don't want with the base classes. A developer is free to implement the data store however they wish (ICRUD can talk to a webservice, for instance) with no knowledge of Entity, or they're also free to augment behavior provided by the BaseRepository by overriding any of the provided methods and doing something differently.
personally i would encourage you guys to not create anything, just use the dbContext, it has all the methods you need anyways.
I myself implemented #1 (implementing IRepository), but you would end up doing some funky programming to get to the correct ObjectSet or EntitySet to add or delete from your Add/Delete methods.
That code would keep becoming more complex as you add inheritance hierarchies in your objectmodel.