A while back I published an article which discussed how to use the then WCF web API with UnitOfWork/Respository, and one user asked me how to do this with ASP MVC4, where the WCF Web API has now become the WebAPI, which you can use by creating a new ApiController. Its pretty simple. To do this lets consider a simple ApiController which is as follows:
public class ProductsController : ApiController { private readonly IUnitOfWork unitOfWork; private readonly IRepository productRepository; public ProductsController(IUnitOfWork unitOfWork, IRepository productRepository) { if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); } if (productRepository == null) { throw new ArgumentNullException("productRepository"); } this.unitOfWork = unitOfWork; this.productRepository = productRepository; } public IEnumerable GetAllProducts() { productRepository.EnrolInUnitOfWork(unitOfWork); return productRepository.FindAll().AsEnumerable(); } public Product GetProduct(int id) { productRepository.EnrolInUnitOfWork(unitOfWork); Product item = productRepository.FindBy(x => x.Id == id).SingleOrDefault(); if (item == null) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)); } return item; } public IEnumerable GetProductsByCategory(string category) { productRepository.EnrolInUnitOfWork(unitOfWork); return productRepository.FindBy(x=> string.Equals(x.Category, category, StringComparison.OrdinalIgnoreCase)); } public HttpResponseMessage PostProduct(Product item) { productRepository.EnrolInUnitOfWork(unitOfWork); Product newItem = productRepository.Add(item); unitOfWork.Commit(); var response = Request.CreateResponse(HttpStatusCode.Created, newItem); string uri = Url.Link("DefaultApi", new { id = item.Id }); response.Headers.Location = new Uri(uri); return response; } public void PutProduct(int id, Product product) { productRepository.EnrolInUnitOfWork(unitOfWork); Product item = productRepository.FindBy(x => x.Id == id).SingleOrDefault(); if (item != null) { item.Name = product.Name; item.Category = product.Category; item.Price = product.Price; unitOfWork.Commit(); } } public HttpResponseMessage DeleteProduct(int id) { productRepository.EnrolInUnitOfWork(unitOfWork); Product item = productRepository.FindBy(x => x.Id == id).SingleOrDefault(); if (item != null) { productRepository.Remove(item); unitOfWork.Commit(); } return new HttpResponseMessage(HttpStatusCode.NoContent); } }
Where you can clearly see this guy takes some dependenices which look like this
UnitOfWork
I am using a Entity Framework code first Unit of work abstraction. The general idea I was going for is that my repositories can involve in a UnitOfWork, and the UnitOfWork is committed when the work is well um done.
public interface IUnitOfWork : IDisposable { void Commit(); void Attach(T obj) where T : class; void Add(T obj) where T : class; IQueryable Get() where T : class; bool Remove(T item) where T : class; }
public abstract class EfDataContextBase : DbContext, IUnitOfWork { public IQueryable Get() where T : class { return Set(); } public bool Remove(T item) where T : class { try { Set().Remove(item); } catch (Exception) { return false; } return true; } public void Commit() { base.SaveChanges(); } public void Attach(T obj) where T : class { Set().Attach(obj); } public void Add(T obj) where T : class { Set().Add(obj); } }
public class ProductContext : EfDataContextBase, IUnitOfWork { public DbSet Products { get; set; } }
Repository
I went for a generic repository, but you may find that you do need a specific repository if these simple methods do not satisfy your needs.
public interface IRepository where T : class { void EnrolInUnitOfWork(IUnitOfWork unitOfWork); int Count { get; } T Add(T item); bool Contains(T item); void Remove(T item); IQueryable FindAll(); IQueryable FindBy(Func<T, bool> predicate); }
public class Repository : IRepository where T : class { protected IUnitOfWork context; public void EnrolInUnitOfWork(IUnitOfWork unitOfWork) { this.context = unitOfWork; } public int Count { get { return context.Get().Count(); } } public T Add(T item) { context.Add(item); return item; } public bool Contains(T item) { return context.Get().FirstOrDefault(t => t == item) != null; } public void Remove(T item) { context.Remove(item); } public IQueryable FindAll() { return context.Get(); } public IQueryable FindBy(Func<T, bool> predicate) { return context.Get().Where(predicate).AsQueryable(); } }
So how do these get satisfied?
Well that is pretty simple, we just need to have a MVC 4 IDependencyScope/IDependencyResolver inheriting class which looks like this. The important thing to note here is that we are creating a new container to service the request for the scope that we get told about by inheriting from IDependencyScope. I went with this approach rather than child containers, which are deprecated in Castle now. I also went for Castle due to its support for open generics, which I needed to have a single generic repository.
class CastleScopeContainer : IDependencyScope, IDependencyResolver { protected IWindsorContainer container; public IDependencyScope BeginScope() { container = new WindsorContainer().Install(new[] { new ApiInstaller() }); ; return this; } public object GetService(Type serviceType) { try { return container.Resolve(serviceType); } catch(Exception ex) { return null; } } public IEnumerable<object> GetServices(Type serviceType) { try { var results = container.ResolveAll(serviceType); if (results == null) return new List<object>(); return results.Cast<object>(); } catch(Exception ex) { return new List<object>(); } } public void Dispose() { container.Dispose(); } }
Where we configure a standard Castle Windsor IOC container using the following installer(s)
public class ApiInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store) { container.Register ( Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>)).LifestyleTransient(), Component.For().ImplementedBy().LifestyleTransient(), Component.For().LifestyleTransient() ); } }
public class DDDInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store) { container.Register ( Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>)).LifestyleTransient(), Component.For().ImplementedBy().LifestyleTransient() ); } }
And the following Global.asax.cs which is used to setup all the IOC requirements for both the WebApi and the ControllerFactory
public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); #region IOC //IOC : I like Castle because it has open generics //Web Api GlobalConfiguration.Configuration.DependencyResolver = new CastleScopeContainer(); //Controllers IWindsorContainer container = new ContainerFactory().Install( new DDDInstaller(), new ControllerInstaller() ); ControllerBuilder.Current.SetControllerFactory(new CastleControllerFactory(container)); #endregion //Seed database Database.SetInitializer(new ProductInitializer()); } }
We can also run a standard controller using IOC, which is easily achievable using a ControllerFactory something like the following:
public class CastleControllerFactory : DefaultControllerFactory { private readonly IWindsorContainer container; public CastleControllerFactory(IWindsorContainer container) { this.container = container; } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { return base.GetControllerInstance(requestContext, null); } object controller= container.Resolve(controllerType); if (controller != null) { return (IController)controller; } return base.GetControllerInstance(requestContext, null); } public override void ReleaseController(IController controller) { var disposable = controller as IDisposable; if (disposable != null) { disposable.Dispose(); } container.Release(controller); } }
Where the ControllerInstaller we previously used in global.asax.cs looks like this
public class ControllerInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( AllTypes.FromAssembly(Assembly.GetExecutingAssembly()) .BasedOn().LifestylePerWebRequest()); } }
Where we may have a controller that looks like this, where we also have the goodness of IOC resolves dependencies in our controllers.
public class HomeController : Controller { private readonly IUnitOfWork unitOfWork; private readonly IRepository productRepository; public HomeController(IUnitOfWork unitOfWork, IRepository productRepository) { if (unitOfWork == null) { throw new ArgumentNullException("unitOfWork"); } if (productRepository == null) { throw new ArgumentNullException("productRepository"); } this.unitOfWork = unitOfWork; this.productRepository = productRepository; } private IEnumerable GetAllProducts() { productRepository.EnrolInUnitOfWork(unitOfWork); return productRepository.FindAll().AsEnumerable(); } public ActionResult Index() { return View(); } public ActionResult ShowDetails() { ViewData["products"] = string.Format("There are currently {0} Products", GetAllProducts().Count()); return View(); } }
I havea small demo project which shows a work demo of all of this available at : http://dl.dropbox.com/u/2600965/Blogposts/2012/07/ProductStoreMVC4Demo.zip