So it’s been a while since I posted here, this has mainly been because I’ve just been too busy with numerous things. In my personal life I’ve moved from one side of the Netherlands to the other and in my professional life I’ve been involved with a large scale enterprise Silverlight application for medicinal logistics. While this has been going on I’ve been immersing myself in the wonderful world of Web Application Development, ASP.NET MVC and numerous JavaScript frameworks, to prepare myself for the development of next generation Rich Internet Applications using HTML and JavaScript. But of course, these RIA’s also need to get their data from somewhere. The ASP.NET Web API is probably one of the nicest ways to implement your REST services for a RIA. In a loosely coupled application, Dependency Injection (DI) is a must and this can be a bit painful in the ASP.NET Web Api if you don’t know where to look. This post is going to tell you exactly how to use the same in DI container in your MVC Controllers and your Web Api controllers, so you can share the same set of services. Of course after you have seen this, it will be immediately clear how to use different containers in both, if you like to do so. The example will be implemented using the Repository pattern, AutoFac, Entity Framework 5 and the EF powertools.
Setting things up
Fire up Visual Studio 2012 RC and start a new MVC 4 empty project:
Call it anything you like. After Visual Studio is done creating your project layout, we’re going to implement the Repository pattern. In a production application you’ll probably want to split your solution into multiple projects, but for now we’re going to do everything in one. First, make sure you have installed the Entity Framework powertools using the Visual Studio extension manager:
After this, use NuGet to add EF 5.0 support to your MVC project:
If you don’t see the PreRelease version, make sure to set the combobox in the top of the screen to “Include Prerelease”. There is one last thing left to do to complete the setup and that’s adding a DI container to our project. You can of course use anything you like, but I’m going with AutoFac. If you want to find out why you should use AutoFac too you can read this. In short, AutoFac combines a full feature set with great performance, is easy to configure and has great support. You can use NuGet to add AutoFac to your project:
Make sure you get the “MVC 4 RC Integration” package. This will provide you with easy integration and will also install the basic AutoFac DLL’s. That’s it, now we’ve got everything we need (assuming you already have a database).
Creating the repository
Create the following interface:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Adventureworks.DAL.Repository { public interface IRepository<in TKey,TEntity> { void Add(TEntity entity); void Delete(TEntity entity); void Update(TEntity entity); IEnumerable<TEntity> GetAll(); TEntity GetById(TKey id); } }
Now let’s implement it using EF 5.0 and the powertools. I really like the Code only feature of the new Entity Framework release, totally removing the .edmx file. But until recently you couldn’t reverse engineer code only from an existing database. Luckily the EF powertools fix this for us. Right click your Web Project and go the “Entity Framework” menu and select “Reverse engineer Code first”:
Select the database of your choosing and let the tooling do it’s magical stuff. After all is said and done, you will have Entity classes, a DBContext and a file containing the code for configuring the DbContext. Create a class which implements the IRepository interface like this:
using System; using System.Collections.Generic; using System.Data.Entity.Infrastructure; using System.Linq; using System.Text; using System.Threading.Tasks; using Adventureworks.Domain; namespace Adventureworks.DAL.Repository.EntityFramework { public class EntityFrameworkProductRepository : IRepository<int,Product> { public void Add(Product entity) { PerformAction((context) => { context.Product.Add(entity); context.SaveChanges(); }); } public void Delete(Product entity) { PerformAction((context) => { context.Product.Attach(entity); context.Product.Remove(entity); context.SaveChanges(); }); } public void Update(Product entity) { PerformAction((context) => { context.Product.Attach(entity); context.Entry(entity).State = System.Data.EntityState.Modified; context.SaveChanges(); }); } public IEnumerable<Product> GetAll() { return Read((context) => { return context.Product.AsNoTracking().ToArray(); }); } public Product GetById(int id) { return Read((context) => { Product p = context.Product.AsNoTracking().SingleOrDefault((pr) => pr.ProductID == id); if (p == null) { throw new ArgumentException("Invalid id: " + id); } return p; }); } private void PerformAction(Action<AdventureWorks2012Entities> toPerform) { using (AdventureWorks2012Entities ents = new AdventureWorks2012Entities()) { ConfigureDbContext(ents); toPerform(ents); } } private T Read<T>(Func<AdventureWorks2012Entities, T> toPerform) { using (AdventureWorks2012Entities ents = new AdventureWorks2012Entities()) { ConfigureDbContext(ents); return toPerform(ents); } } private void ConfigureDbContext(AdventureWorks2012Entities ents) { ents.Configuration.AutoDetectChangesEnabled = false; ents.Configuration.LazyLoadingEnabled = false; ents.Configuration.ProxyCreationEnabled = false; ents.Configuration.ValidateOnSaveEnabled = true; } } }
There are a couple of things going on here. Starting on line 66 I’ve created three helper methods which set up the DbContext correctly and dispose it. These methods are used by calling them and supplying a Lambda which uses the DbContext. Let’s take a look at the GetAll method on line 44. You can see that I don’t use change tracking. Change tracking is something you get as a bonus when using the EF, I like to abstract this away with my Repository implementation. It’s also completely useless in a web application since all state is gone after each request and it has a lot of overhead. “But how do you update if you don’t have any change tracking?” you ask?, well take a look at the Update method on line 34. Just set the whole entity as “Modified” and the EF will perform an update for you.
Creating a Web API Controller
Now let’s create a Web API controller to perform some CRUD functionality:
Implement it like this:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using Adventureworks.DAL.Repository; using Adventureworks.Domain; namespace Adventureworks.Web.Controllers { public class ProductController : ApiController { private IRepository<int, Product> _productRepository; public ProductController(IRepository<int,Product> repository) { _productRepository = repository; } public IEnumerable<Product> Get() { return _productRepository.GetAll(); } public Product Get(int id) { try { return _productRepository.GetById(id); } catch (ArgumentException ex) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound){ Content = new StringContent(ex.Message)}); } } // POST api/product public HttpResponseMessage Post(Product product) { ValidateProduct(); _productRepository.Add(product); return new HttpResponseMessage(HttpStatusCode.Created) { Content = new StringContent(Url.Route("DefaultApi", new{controller="Product",id=product.ProductID}))}; } // PUT api/product/5 public HttpResponseMessage Put(Product product) { ValidateProduct(); _productRepository.Update(product); return new HttpResponseMessage(HttpStatusCode.NoContent); } // DELETE api/product/5 public HttpResponseMessage Delete(Product product) { _productRepository.Delete(product); return new HttpResponseMessage(HttpStatusCode.NoContent); } private void ValidateProduct() { if (!ModelState.IsValid) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest)); } } } }
Let’s go to line 65 first. This is a helper method which uses the built in Model Binding feature to validate the incoming product. This means that you can just decorate your Product class with attributes or implement IValidatable object to implement data validation. Keeping up with the spirit of rest, this method will generate a BadRequest statuscode when the incoming product is invalid. Now jump up to the constructor. The controller only works with an IRepository interface to perform the crud functionality, it never knows anything about the Entity Framework. This is the key advantage of DI, as we can now mock the repository and unittest our controller. Now jump to line 42; The Post method. A post in REST means an insert. It’s also in the spirit of REST that you use the HTTP statuscodes to signal what’s going on. When you create new content, you should provide the caller with an url to the new content. Similar to the Post method, you can see that the other methods also use statuscodes to indicate if everything went well or not.
Wiring everything up
Last thing left to do is to configure our container and integrate it with MVC. Here’s my Global.asax:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; using Adventureworks.DAL.Repository.EntityFramework; using Adventureworks.Web.Services; using Autofac; using Autofac.Integration.Mvc; using Autofac.Integration.WebApi; namespace Adventureworks.Web { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); var builder = new ContainerBuilder(); builder.RegisterControllers(typeof(MvcApplication).Assembly); builder.RegisterApiControllers(typeof(MvcApplication).Assembly); builder.RegisterType<EntityFrameworkProductRepository>().AsImplementedInterfaces().InstancePerApiRequest().InstancePerHttpRequest(); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container); } } }
First up are lines 28-32. This is the configuring of the AutoFac container. You register all the controllers for MVC and the Web API with two lines of code. This is done on lines 29 and 30. On line 31 I am registering the EntityFrameworkProductRepository in a per request scope, for MVC controllers and Web API controllers. On line 32 the container is built. On line 35 the container is registered for MVC controllers. On line 36 it’s registered for API controllers. This is what bites people the most. To use DI with MVC, you need a class which implements IDependencyResolver. To use DI with the ASP.NET Web API, you also need a class which implements IDependencyResolver. But these interfaces aren’t the same and they live in different namespaces. The dependency resolvers are also registered differently as you can see on lines 35 and 36. Luckily, AutoFac’s MVC integration package which we installed earlier, contains dependency resolvers for use to use, otherwise we had to implement these ourselves. That’s all! Now go out and test your REST service with your favorite tool .