Enterprise Asp.Net MVC Part 4: Repository
Posted on: 2012-11-02
This is the forth part of the series concerning enterprise Asp.Net MVC web site. In this article, we will discuss about how to design the repository. Has you can imagine, we won't use Entity Framework (or any other ORM) directly into controllers. Also, this article will focus on Entity Framework 5.0 but the concept behind is the same : the repository must be abstracted from the controller. The main reason is that we want to be able to respect the single responsibility principle. The controller responsibility is not about how to load or save entity but to know how to dispatch. This why we will follow separation of concern idea by having classes that will handle the repository. By separating the repository we will use many classes to have a set of cohesive class. The result will be an application well separated in concern.
Before starting, let me just make one thing clear. I won't abstract Entity Framework here. I do not believe that abstracting the ORM is a good idea. First, it's a lot of overhead. Second, the ORM already abstract the database implementation, and third, it's harder to maintain because if we want something specific to Entity Framework, we will need to do a lot of code.
Abstracting the repository : the plan
The first thing that we need to keep in mind is that every entity will need to use the ORM. Entity Framework use what we call a DbContext. We need to be able to share this DbContext between repositories because we may want to save an entity that will use several other repository. The same is true for loading entity. You may want to load an entity and load a second one in a different repository. Sharing the same DbContext let you have the same transaction when saving and when loading let you have join instead of 2 queries (or more). It also open a single connection to the database instead of several.
The second thing to have in mind is that every entity belong to a user. If UserA create an entity, this entity should belong to him. UserB should access his information. This is not the case of everything, but most of the time yes. Even a Facebook Message is owned by you (but shared to others). So, we need a mechanism to bind data to a user account. Also, we will need to have a way to impersonate in some case this mechanism. This will give us the leverage to save entity to a specific user. A simple case that I can tell you may be to load the database with test data for development purpose. We may want to create entity to several users without being logged to these users.
The third thing to have in mind is that we want to be able to test without having to care about the database. It also mean that I do not want to have overhead when testing by mocking every method of Entity Framework. What we want is to simply mock the repository.
Factory Method Pattern
The factory method pattern let you construct object from a single point. The factory will return all repositories for every entity. It's a central point. The reason to use this pattern is that it will give use a lazy loading for all repositories creation but also will give us the possibility to share the DbContext between those repositories in the creation of them. It also give us the possibility to mock the whole factory for testing. The factory will be the classe shared between all controllers.
Repository Factory
The repository factory inherit from an interface. This interface will contain all entity repository. That mean that if you want to add new entity that you need to add a new entry into this interface.
public interface IRepositoryFactory {
IWorkoutRepository Workout { get; }
IUserProfileRepository UserProfile { get; } //...Other entities...
}
For example, in the code above, we have 2 entities. One is our domain, the Workout class, and the second is the UserProfile that is for the user (from the membership classes). If we wanted to add the Exercises entity, we would need to add a new property in the interface.
You can also notice that IRepositoryFactory contain interface to repository. So, a new entity means a new interface for its repository and for the concrete implementation of this repository.
Before going deeper with the repository class, let check a concrete implementation of IRepositoryFactory for Entity Framework 5.0 Code First approach.
public class RepositoryFactory:IRepositoryFactory {
private readonly IDatabaseContext_databaseContext;
private IWorkoutRepository_workoutRepository;
private IUserProfileRepository_userProfileRepository;
public RepositoryFactory(IDatabaseContext databaseContext) {
_databaseContext = databaseContext;
}
#region Implementation of IRespositoryFactory
public IWorkoutRepository Workout { get { return_workoutRepository ?? (_workoutRepository = new WorkoutRepository(_databaseContext)); } }
public IUserProfileRepository UserProfile { get { return_userProfileRepository ?? (_userProfileRepository = new UserProfileRepository(_databaseContext)); } }
#endregion
}
This class takes in its constructor a IDatabaseContext. This will give us an interface to share between repository. Otherwise, the factory if very simple. It checks if the property has been already initialized, if not, it initialize it with the IDatabaseContext, otherwise, it simply reuse the repository. This class contains for every repository a property. That's it.
Repository Classes
Every repository inherit from IRepository.
public interface IRepository<T> {
IQueryable<T> GetAll(); T Get(int id);
int Insert(T entity);
int Update(T entity);
int Delete(T entity);
}
This give us the 80% repository method that we need. Other more specific method like searching with filter will be added directly into the concrete implementation of the class.
Also, every Repository inherit from a BaseRepository which will hold the DataContext reference. This is required because every call to the repository is done by the DbContext. When the Repository factory pass the IDatabaseContext to the repository, all repository will simply pass the object to the base in their constructor.
public class BaseRepository {
protected IDatabaseContext DatabaseContext { get; private set; }
protected BaseRepository(IDatabaseContext databaseContext) { DatabaseContext = databaseContext; }
}
Here is the example with the Workout entity.
public class WorkoutRepository : BaseRepository, IWorkoutRepository {
public WorkoutRepository(IDatabaseContext databaseContext) : base(databaseContext) { }
#region Implementation of IRepository<Workout>
public IQueryable<Workout> GetAll() { return DatabaseContext.SetOwnable<Workout>().Include(x => x.Sessions); }
public Workout Get(int id) { return DatabaseContext.SetOwnable<Workout>().Include(x => x.Sessions).Single(c => c.Id == id); }
public int Insert(Workout entity) { //To-do : Other stuff with complex type here DatabaseContext.SetOwnable<Workout>().Add(entity); return DatabaseContext.SaveChanges(); }
public int Update(Workout entity) {
Workout fromDatabase = Get(entity.Id);
DatabaseContext.Entry(fromDatabase).CurrentValues.SetValues(entity);
DatabaseContext.Entry(fromDatabase).State = EntityState.Modified;
//To-do : Other stuff with complex type here
return DatabaseContext.SaveChanges();
}
public int Delete(Workout entity) {
DatabaseContext.SetOwnable<Workout>().Remove(entity);
return DatabaseContext.SaveChanges();
}
#endregion
}
It's quite neat! You do not see any detail of database connection.
The only thing we see is task concerning saving and loading entity. We have direct access to Entry and we can use the Set<>
and SetOwnable<>
.
As you can see, we do not need to have any access to the current user, neither to specific to whom the Workout belong because Workout inherit from IUserOwnable.
You will see the detail about how it works on the next article concerning the DbContext (see part 5).
Conclusion
So far, so good. Now we have the controller that talk with the database. We are using Repository factory method to access the desired repository and every repository share the same instance of DbContext which give us the possibility to handle multiple entity with the same context (same transaction). Every classes has its own role. The controller handle http request, the service handle how the database is accessed, the repository factory manage all repositories, repository handle how their entity are stored and finally, the database context take care of the database connection. The next article of the series, part 5 will discuss more in detail about the database context (DbContext) and its role with Entity Framework 5.0.
Series Articles
- Article #1: Asp.Net MVC Enterprise Quality Web Application
- Article #2: Asp.Net MVC Enterprise Quality Web Application Model
- Article #3: Asp.Net MVC Enterprise Quality Web Application Controller
- Article #4: Asp.Net MVC Enterprise Quality Web Repository Layer
- Article #5: Asp.Net MVC Enterprise Quality Web with Entity Framework
- Article #6: Asp.Net MVC Enterprise Quality Layers
- Article #7: Asp.Net MVC Enterprise Quality Web Security