In the past, I have attempted a Unit of Work + Repository architecture. There are arguments that this is duplicate work since DbContext is a UnitOfWork and Repository. I have also found I needed to import the EntityFramework references into my application layer projects (which defeated some of my motivation for abstracting the DbContext).
In my most recent projects I have given up on forcing DbContext into my own Unit of Work and Repository. Now, I extract an IDbContext interface so I can test against mocks more easily.
Package and Framework versions in this article
- MVC5
- WebApi 2
- Entity Framework 6
- Unity 4
- Unity.Mvc
- Unity.AspNet.WebApi
- Moq 4
IDbContext
I abstract my own interface so I can mock and test without an actual database.
public class MovieDbContext : DbContext, IMovieDbContext { public MovieDbContext(string connectionString) : base(connectionString) { // configuration settings } public virtual IDbSet<Movie> Movies { get; set; } public virtual IDbSet<Genre> Genres { get; set; } public override void OnModelCreating(DbModelBuilder modelBuilder) { ... } }
public interface IMovieDbContext : IDisposable { IDbSet<Movie> Movies { get; set; } IDbSet<Genre> Genres { get; set; } int SaveChanges(); }
Since I’m extending DbContext
, IDisposable
and SaveChanges()
is implemented for me. I add to the interface if my application requires more of the DbContext’s methods.
I Moq this up on my test class
protected virtual IMovieDbContext GetMockContext(IList<Movie> movies, IList<Genre> genres) { var dbSetMovies = GetMockDbSet<Movie>(movies); var dbSetGenres = GetMockDbSet<Genre>(genres); var mockContext = new Mock<IMovieDbContext>(); mockContext.Setup(m => m.Movies).Returns(dbSetMovies); mockContext.Setup(m => m.Genres).Returns(dbSetGenres); return mockContext.Object; } protected virtual IDbSet<T> GetMockDbSet<T>(IList<T> data) where T : class { var queryable = data.AsQueryable(); var mockSet = new Mock<IDbSet<T>>(); mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider); mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression); mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType); mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator()); mockSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => data.Add(s)); return mockSet.Object; }
Service Layer
I now can segregate the query code into a service layer. I usually use AutoMapper, but for this example have manual projections.
public class MovieService : IMovieService { private IMovieDbContext db; public MovieService(IMovieDbContext db) { this.db = db; } public MovieDto GetMovie(string title) { var query = db.Movies.Where(m => m.Title == title) .Select(m => new MovieDto { Id = m.Id, Title = m.Title, Year = m.Year }); var movie = query.FirstOrDefault(); return movie; } }
The service easily takes a DbContext mock to test the query logic without connecting to a database.
Controllers
The controller doesn’t care about the DbContext nor its query logic. The service is injected with the IoC container (Unity).
public class MovieController : Controller { private IMovieService service; public MovieController(IMovieService service) { this.service = service; } public ActionResult GetMovie(string title) { var movie = this.service.GetMovie(title); return View("MovieDetail", movie); } }
Unity IoC
I include the Unity.Mvc and Unity.AspNet.WebApi bootstrappers which provide the UnityMvcActivator.cs and UnityWebApiActivator.cs files. I uncomment the lines to get the per request lifetime object management.
// UnityMvcActivator.cs // TODO: Uncomment if you want to use PerRequestLifetimeManager Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); // UnityWebApiActivator.cs // Use UnityHierarchicalDependencyResolver if you want to use a new child container for each IHttpController resolution. var resolver = new UnityHierarchicalDependencyResolver(UnityConfig.GetConfiguredContainer());
And finally, register your types with the container in UnityConfig.cs.
public static RegisterTypes(IUnityContainer container) { container.RegisterType<IMovieDbContext, MovieDbContext>(new PerRequestLifetimeManager(), new InjectionConstructor("name=MovieDbConnection"); container.RegisterType<IMovieService, MovieService>(new PerRequestLifetimeManager()); }
Now I have a testable application without a hard dependency on the database. I still need the EntityFramework reference in the Web project but my controllers don’t really know that.