MVC Repository with Unit Of Work, Automapper and Generic Repository

asp.net-mvc, automapper, entity-framework-5, repository-pattern, unit-of-work

Solution

Don't use generic repositories. They are all leaky abstractions. Ask yourself, what benefit to you get by using an abstraction that doesn't really abstract away something? You could use your OR/M directly in those cases.

What I means is that anything that exposes `IQueryable<T>` forces the user to learn about the weaknesses that the underlying OR/M has. Examples: How do the orm handle lazy loading? How do I eagerly load related entities? How do I create a `IN` clause?

If you truly want to use the repository pattern either use it together with the specification pattern (you can keep on using a generic repository then) or create repositories that are specific for each root aggregate.

I've blogged about it: http://blog.gauffin.org/2013/01/repository-pattern-done-right/

Problem

I've been looking at a few blog posts to try and create an appropriate solution for the following requirements but I can't seem to piece them together. Hope fully someone can help. I've been using Repository pattern with interfaces using Automapper...here's a trimmed down example: ``` public class BookingRepository : IBookingRepository { Entities context = new Entities(); public IEnumerable<BookingDto> GetBookings { get { return Mapper.Map<IQueryable<Booking>, IEnumerable<BookingDto>>(context.Bookings); } } public BookingDto GetBookingWithProduct(Guid bookingId) { return Mapper.Map<BookingDto>(context.Bookings.Include(c => c.Products).SingleOrDefault(c => c.BookingId == bookingId)); } public void Update(BookingDto bookingDto) { var booking = Mapper.Map<Booking>(bookingDto); context.Entry(booking).State = EntityState.Modified; } public void Save() { context.SaveChanges(); } public void Dispose() { context.Dispose(); } } public interface IBookingRepository : IDisposable { IEnumerable<BookingDto> GetBookings { get; } BookingDto GetBooking(Guid bookingId); void Update(BookingDto bookingDto); void Save(); } ``` With a seperate Repository for a different Entity, for example ``` public class ProductRepository : IProductRepository { Entities context = new Entities(); public IEnumerable<ProductDto> GetProducts { get { return Mapper.Map<IQueryable<Product>, IEnumerable<ProductDto>>(context.Products); } } public ProductDto GetProductWithDesign(int productId) { return Mapper.Map<ProductDto>(context.Products.Include(c => c.Designs).SingleOrDefault(c => c.ProductId == productId)); } public void Update(ProductDto productDto) { var product = Mapper.Map<Product>(productDto); context.Entry(product).State = EntityState.Modified; } public void Save() { context.SaveChanges(); } public void Dispose() { context.Dispose(); } } public interface IProductRepository : IDisposable { IEnumerable<ProductDto> GetProducts { get; } ProductDto GetProduct(int productId); void Update(ProductDto productDto); void Save(); } ``` Then in my Controller I'm using the repositories as so: ``` public class HomeController : Controller { private readonly IBookingRepository bookingRepository; private readonly IProductRepository productRepository; public HomeController() : this(new BookingRepository(), new ProductRepository()) { } public HomeController(IBookingRepository bookingRepository, IProductRepository productRepository) { this.bookingRepository = bookingRepository; this.productRepository = productRepository; } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing && this.bookingRepository != null) this.bookingRepository.Dispose(); if (disposing && this.productRepository != null) this.productRepository.Dispose(); } } ``` So now I'm hoping to create a Unit Of Work to abstract these repositories and share the context and also create a generic repository for the duplicated actions (Save and Update) bearing in mind I'm passing in Dtos and Mapping to Entity objects. I'm having difficulty understanding how to knit it all together. Additionally, I've seen this post Repository pattern with generics and DI which states "You should not have other repository interfaces besides your generic repository" and that custom queries "deserve their own (generic) abstraction:" which is adding another complication to my overworked brain as my repositories will have custom queries that return complex linked objects using Include Statements as Lazy Loading is disabled. So I'm prepared to be shot down and told that I'm going about this the wrong way but would be grateful for any direction given. Thanks in advance.

Original source