In this post, we will see how to implement a decoupled, unit-testable, N tier architecture based on Generic Repository Pattern with Entity Framework, IoC Container and Dependency Injection in ASP.NET MVC. We will implement a sample application step by step for the same.
Note: If you think repository pattern on top of Entity Framework is redundant and want to skip it in N Layered application, refer following post:
N-Layered App with Entity Framework, Autofac, ASP.NET MVC and Unit Testing
Technologies/Tools used for this sample:
1. .NET Framework 4.5
2. ASP.NET MVC 5.1
3. Entity Framework 6.0
4. Sql Server LocalDB v11
5. Autofac 3.4
5. Moq
6. Effort
7. Visual Studio 2012
Objective:
1. Keep a clean separation of concerns.
2. Make unit testing and integration testing easy
3. A complete example on Repository and Unit of Work with IoC and Unit Testing.
Repository Pattern: To create an abstract data access layer for the application which allows to centralise all data access logic in one place. With generic feature, we can reduce the amount of code we need for common scenarios.
Unit of Work pattern: To combine a set of interactions and commit them at once using a transaction.
It is assumed you know the basics of Repository pattern, IoC and DI pattern. For more information, see the following resources:
The Repository Pattern on MSDN.
Design pattern – Inversion of control and Dependency injection
The implementation contains different layers:
1. Database (to manage data)
2. Data Access Layer (contains EF LINQ queries, models and datacontext)
3. Service Layer (Business and domain Logic)
4. MVC Web Layer (UI part which talks to service layer only)
5. Test Layer (for unit testing…etc)
Setting up the Solution:
1. Open Visual Studio, Create new “ASP.NET MVC 5 Empty Project” Say “SampleArch“.
2. In the solution, Add following “Class Library” Projects
SampleArch.Model
SampleArch.Repository
SampleArch.Service
Remove Class1.cs which is created by default.
3. Add project > Test > “Unit Test project” with following name
SampleArch.Test
Remove UnitTest1.cs which is created by default.
The Model:
First install EntityFramework, run the following command in the Package Manager Console
Install-Package EntityFramework
In “SampleArch.Model” project, Add following base classes
IEntity.cs:
public interface IEntity<T> { T Id { get; set; } }
Entity.cs:
public abstract class BaseEntity { } public abstract class Entity<T> : BaseEntity, IEntity<T> { public virtual T Id { get; set; } }
IAuditableEntity.cs:
public interface IAuditableEntity { DateTime CreatedDate { get; set; } string CreatedBy { get; set; } DateTime UpdatedDate { get; set; } string UpdatedBy { get; set; } }
AuditableEntity.cs:
public abstract class AuditableEntity<T> : Entity<T>, IAuditableEntity { [ScaffoldColumn(false)] public DateTime CreatedDate { get; set; } [MaxLength(256)] [ScaffoldColumn(false)] public string CreatedBy { get; set; } [ScaffoldColumn(false)] public DateTime UpdatedDate { get; set; } [MaxLength(256)] [ScaffoldColumn(false)] public string UpdatedBy { get; set; } }
ScaffoldColumn(false) is used So that ASP.NET MVC Scaffolding will NOT generate controls for this in Views. We will handle these properties in context SaveChanges method.
Lets add Person and Country Models:
[Table("Person")] public class Person : AuditableEntity<long> { [Required] [MaxLength(50)] public string Name { get; set; } [Required] [MaxLength(20)] public string Phone { get; set; } [Required] [MaxLength(100)] public string Address { get; set; } [Required] [MaxLength(50)] public string State { get; set; } [Display(Name="Country")] public int CountryId { get; set; } [ForeignKey("CountryId")] public virtual Country Country { get; set; } }
public class Country : Entity<int> { [Required] [MaxLength(100)] [Display(Name="Country Name")] public string Name { get; set; } public virtual IEnumerable<Person> Persons { get; set; } }
Adding DB Context:
public class SampleArchContext : DbContext { public SampleArchContext() : base("Name=SampleArchContext") { } public DbSet<Person> Persons { get; set; } public DbSet<Country> Countries { get; set; } public override int SaveChanges() { var modifiedEntries = ChangeTracker.Entries() .Where(x => x.Entity is IAuditableEntity && (x.State == System.Data.Entity.EntityState.Added || x.State == System.Data.Entity.EntityState.Modified)); foreach (var entry in modifiedEntries) { IAuditableEntity entity = entry.Entity as IAuditableEntity; if (entity != null) { string identityName = Thread.CurrentPrincipal.Identity.Name; DateTime now = DateTime.UtcNow; if (entry.State == System.Data.Entity.EntityState.Added) { entity.CreatedBy = identityName; entity.CreatedDate = now; } else { base.Entry(entity).Property(x => x.CreatedBy).IsModified = false; base.Entry(entity).Property(x => x.CreatedDate).IsModified = false; } entity.UpdatedBy = identityName; entity.UpdatedDate = now; } } return base.SaveChanges(); } }
Here we are setting audit column values by overriding SaveChanges method.
Now in web.config of asp.net MVC “SampleArch” project, add connectionstring named “SampleArchContext”.
<connectionStrings> <add name="SampleArchContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=SampleArch;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\SampleArch.mdf" providerName="System.Data.SqlClient"/> </connectionStrings>
Adding Repository:
In “SampleArch.Repository” project, Build and Add reference of “SampleArch.Model” project.
Add following base classes:
IGenericRepository.cs
public interface IGenericRepository<T> where T : BaseEntity { IEnumerable<T> GetAll(); IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate); T Add(T entity); T Delete(T entity); void Edit(T entity); void Save(); }
GenericRepository.cs
public abstract class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity { protected DbContext _entities; protected readonly IDbSet<T> _dbset; public GenericRepository(DbContext context) { _entities = context; _dbset = context.Set<T>(); } public virtual IEnumerable<T> GetAll() { return _dbset.AsEnumerable<T>(); } public IEnumerable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate) { IEnumerable<T> query = _dbset.Where(predicate).AsEnumerable(); return query; } public virtual T Add(T entity) { return _dbset.Add(entity); } public virtual T Delete(T entity) { return _dbset.Remove(entity); } public virtual void Edit(T entity) { _entities.Entry(entity).State = System.Data.Entity.EntityState.Modified; } public virtual void Save() { _entities.SaveChanges(); } }
Note: I didn’t use IQueryable for public methods because EF queries should NOT be made outside of repository layer.
Create repository of Person and Country:
IPersonRepository.cs
public interface IPersonRepository : IGenericRepository<Person> { Person GetById(long id); }
PersonRepository.cs
public class PersonRepository : GenericRepository<Person>, IPersonRepository { public PersonRepository(DbContext context) : base(context) { } public override IEnumerable<Person> GetAll() { return _entities.Set<Person>().Include(x=>x.Country).AsEnumerable(); } public Person GetById(long id) { return _dbset.Include(x=>x.Country).Where(x => x.Id == id).FirstOrDefault(); } }
Here we did override GetAll method to include country.
ICountryRepository.cs
public interface ICountryRepository : IGenericRepository<Country> { Country GetById(int id); }
CountryRepository.cs
public class CountryRepository : GenericRepository<Country>, ICountryRepository { public CountryRepository(DbContext context) : base(context) { } public Country GetById(int id) { return FindBy(x => x.Id == id).FirstOrDefault(); } }
Unit Of Work:
IUnitOfWork.cs
public interface IUnitOfWork : IDisposable { /// <summary> /// Saves all pending changes /// </summary> /// <returns>The number of objects in an Added, Modified, or Deleted state</returns> int Commit(); }
UnitOfWork.cs
/// <summary> /// The Entity Framework implementation of IUnitOfWork /// </summary> public sealed class UnitOfWork : IUnitOfWork { /// <summary> /// The DbContext /// </summary> private DbContext _dbContext; /// <summary> /// Initializes a new instance of the UnitOfWork class. /// </summary> /// <param name="context">The object context</param> public UnitOfWork(DbContext context) { _dbContext = context; } /// <summary> /// Saves all pending changes /// </summary> /// <returns>The number of objects in an Added, Modified, or Deleted state</returns> public int Commit() { // Save changes with the default options return _dbContext.SaveChanges(); } /// <summary> /// Disposes the current object /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Disposes all external resources. /// </summary> /// <param name="disposing">The dispose indicator.</param> private void Dispose(bool disposing) { if (disposing) { if (_dbContext != null) { _dbContext.Dispose(); _dbContext = null; } } } }
Service Layer:
In SampleArch.Service project, add references of Model and Repository projects, add following base services class:
IService.cs
public interface IService { }
IEntityService.cs
public interface IEntityService<T> : IService where T : BaseEntity { void Create(T entity); void Delete(T entity); IEnumerable<T> GetAll(); void Update(T entity); }
EntityService.cs
public abstract class EntityService<T> : IEntityService<T> where T : BaseEntity { IUnitOfWork _unitOfWork; IGenericRepository<T> _repository; public EntityService(IUnitOfWork unitOfWork, IGenericRepository<T> repository) { _unitOfWork = unitOfWork; _repository = repository; } public virtual void Create(T entity) { if (entity == null) { throw new ArgumentNullException("entity"); } _repository.Add(entity); _unitOfWork.Commit(); } public virtual void Update(T entity) { if (entity == null) throw new ArgumentNullException("entity"); _repository.Edit(entity); _unitOfWork.Commit(); } public virtual void Delete(T entity) { if (entity == null) throw new ArgumentNullException("entity"); _repository.Delete(entity); _unitOfWork.Commit(); } public virtual IEnumerable<T> GetAll() { return _repository.GetAll(); } }
This is common implementation for the services. Lets create Service classes for Country and Person.
ICountryService.cs
public interface ICountryService : IEntityService<Country> { Country GetById(int Id); }
CountryService.cs
public class CountryService : EntityService<Country>, ICountryService { IUnitOfWork _unitOfWork; ICountryRepository _countryRepository; public CountryService(IUnitOfWork unitOfWork, ICountryRepository countryRepository) : base(unitOfWork, countryRepository) { _unitOfWork = unitOfWork; _countryRepository = countryRepository; } public Country GetById(int Id) { return _countryRepository.GetById(Id); } }
IPersonService.cs
public interface IPersonService : IEntityService<Person> { Person GetById(long Id); }
PersonService.cs
public class PersonService : EntityService<Person>, IPersonService { IUnitOfWork _unitOfWork; IPersonRepository _personRepository; public PersonService(IUnitOfWork unitOfWork, IPersonRepository personRepository) : base(unitOfWork, personRepository) { _unitOfWork = unitOfWork; _personRepository = personRepository; } public Person GetById(long Id) { return _personRepository.GetById(Id); } }
Things To Note:
1. Constructor of service layer takes UnitOfWork and Repository objects.
2. Service layer can’t access databasecontext object means LINQ query on top of entity framework can’t be applied here which makes clean separation of concern.
In next part, we will setup Autofac and Implement CRUD operations in MVC project using these layers. You can get source code in last part of the series.
Feel free to share your thoughts in the comment box.
You missed the interesting part , the dependency inject config.
With your implementation, if you make the DbContext Transient. the transaction are not guaranteed anymore. You commit on the different DbContext instance than repositories
Yes. My unit of work is not saving data to database. I think at unit of work new instance has created this issue. Can you help me at this issue ? May be dependency injection ?
Perfect, this is what I’ve been searching for a decade. With this handful examples I think I have learned enough to get started. Thanks.
what is the responsibility of IPersonRepository and IPersonService ? are they doing same work?
Generally Repository is related to do CRUD operations of Model while service is to implement business rules/logics. It might include multiple repositories.
What is the purpose of the unit of work? It seem useless if my controller/application is not using it. I could simply put the save/commit function in my generic repository.
Unit of work purpose is to “wrap” single bussiness transaction (usually with DB I would say). Then you can stack transactions and execute them one by one in the same order they were formed. It ensures that you always work with consistent (DB) data in the case you somehow changed same entity on two places… but actually I learn this last few weeks, so I might not be 100% correct/accurate.
Is someone answering the questions here? I see a lot of interesting questions but no answers :(
For example, I would say that you have an issue if you want to reuse the same Unit of Work on multiple transactions with this approach. But perhaps a small tweak can solve this problem.
Sorry but it’s not a good article, you have a good idea, but you have a problem, UoW it’s for supports transacctions.
can i use web api 2 instead mvc web layer with autofac modules?
I am really interested in how you setup your model layer, but have a question. I am a bit confused to why there is an Entity and BaseEntity. I understand that this is the lowest level so anything that all entities will have in common will go here, but why do we need both Entity and BaseEntity and can you give me an example of what kind of items would be put in the empty BaseEntity class? It just seems a bit overkill to have both.
Nice article. Justa couple fo questions:
1) Do you have a smaple code project for download?
2) What is the purpose of IService? I can’t see why you would need that interface unless you want to extend but you are already doing that with ICountryService etc?
A very good article. One question I have.
The service layer is a just another abstraction over repository class. Its primary purpose is to supports transactions on multiple repositories via UnitOfWork. Can we not eliminate this and alternatively
1. either let the UnitOfwork keep track of repositories or
2. Pass the UnitOfWork to repositories so that they can support transactions(which is essentially 95% same approach as this class).
I can see the benefit of servicelayer that it can abstract out the DbContext and application layer further but while creating the service layer, we anyways have to pass the Repository class which would expect me to pass a DbContext in its constructor. So the application actually knows about the DbContext too.
For a stateful application, this is an overkill since we can do away with a simple wrapper on top of DbContext and a mapper to map domain entities to data entities(EF ones). For a stateless application, i would use this but in such a way that my application knows nothing about the DbContext. so it will be something like the implementation shown here: http://rahulrajatsingh.com/2014/06/understanding-repository-and-unit-of-work-pattern-and-implementing-generic-repository-in-asp-net-mvc-using-entity-framework/
A question,
imagine that when I add a person, in the same transaction update another different table. As you would??
Hi I liked this Architecture it’s actually very clean, but I don’t understand why the IQueryable shouldn’t be used outside of repository. I would like to implement one Architecture like this but using filters on list (Index) and pagination, and the filter I used to do with IQueryable, any suggestion to implement something like that?
Thanks
Making an abstraction over an abstraction is quite overkill, for me a dbcontext like your example is already an unit of work, so why bother encapsulate it again. By all means previously i used repository approach, somehow i found it can quite become messy especially when you are dealing with multiple repository and doing interaction between them. I found this, http://rob.conery.io/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/, approach has much more cleaner way by using CQRS.
public override IEnumerable GetAll()
{
return _entities.Set().Include((string) (x => x.Country)).AsEnumerable();
}
x.Country error
You put the Context in your model project. But this is the domain of your application and so it should know nothing about EF or context. The context should be in your repository project since it is responsible to work with EF. Why did you add EF to the model domain and how to refactor in order to autofac see it in the right place (repository) ?
Please make a video Tutorial. If you have already one pls share the link.
Hi, how would you handle an update on two entities in the same unit of work? Thanks in advance.
Another thing i noticed we may need use ModelState on Service for business logic validations. So is there a way to inject that with Autofac too?
Very good example to get started!
On this behaviour what should we doo if need a service inside another service?
I think instead of using service we can use Repository for CRUD operations. Please correct me if I am wrong.
It depends…Testing on a generic repository is easier, and decouples the dependency on Entity Framework.
Hi Brij, when you have some time please take a look in this content: Is the Repository pattern useful with Entity Framework? http://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework-part-2/
Thanks for your article.
Yes, I’m in a greenfield project and plan to ditch this pattern. It’s a nightmare of abstraction upon abstraction upon abstraction. Abstraction to a certain extent is good, beyond this extent is pure madness.
You can find in Part 4 of this series:
http://techbrij.com/generic-repository-unit-testing-ioc-di-asp-net-mvc
Its very nice sample, can I get code for the same. Thanks
Generic repository is not a good idea for following reasons- http://www.wekeroad.com/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/
I like clean and simple and a good read for someone who’s an aspiring solutions architect such as me.