Archive

Posts Tagged ‘MCV 3’

Pro ASP.NET MVC 3 Framework

December 27, 2011 Leave a comment
December 27th, 2011 | Kevin Kershaw

Pro ASP.NET MVC 3 Framework

This is a brief review Pro ASP.NET MVC 3 Framework by Adam Freeman and Steven Sanderson. Now in its third edition this already good book continues to get better. This book provides thorough and comprehensive of MVC3 that functions well for both learning the subject and as a reference. This book includes a substantial example web site that is developed through the course of three chapters. This sample covers many aspects and issues that you would encounter developing a web site using MCV 3. Also within this sample are several interesting methods and techniques. I am selecting two that I found most interesting to explore below.

Using DI And Mock Objects To Replace DB And Repository Code

Many of the projects and features I have worked on have proceeded from the database first and then built code towards the UI. The technique below allows the UI to be developed earlier in the cycle and can facilitate prototyping the UI with a lower investment in backend code. First some infrastructure setup is needed. We will start by defining a controller factory that uses the Ninject dependency injector .

public class NinjectControllerFactory : DefaultControllerFactory
{
    private IKernel ninjectKernal;

    public NinjectControllerFactory()
    {
        ninjectKernal = new StandardKernel();
        AddBindings();
    }

    protected override IController GetControllerInstance(
        System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        return controllerType == null ? null :
             (IController)ninjectKernal.Get(controllerType);
    }
    ...
}

 

Wire this controller factory in by replacing the default controller factory in Global.asx

    protected void Application_Start()
    {
        ...
        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
        ...
    }

This infrastructure is desirable in the project by itself. Its existence will eliminate much trivial and annoying code. The fact that it also supports the technique we are discussing is just a bonus.

We will continue by defining a DTO that will be used by the front end.

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
}

 

Next, define the repository interface that will be used by the controllers to access the data.

 

public interface IProductRepository
{
    IQueryable<Product> Products { get; }
}

Finally, we will setup a fake repository using Moq and wire it into the Ninject container. Add to the NinjectControllerFactory defined above the using the following function.

private void AddBindings()
{
    Mock<IProductRepository> mock = new Mock<IProductRepository>();
    mock.Setup(m => m.Products).Returns(new List<Product>{
        new Product{Name="Football", Price=25, Description="Standard issue ball"},
        new Product{Name="Surf board", Price=179, Description="A wave rider you will love"},
        new Product{Name="Running shoes", Price=95, Description="Move your fat butt!"}
        }.AsQueryable());
        ninjectKernal.Bind<IProductRepository>().ToConstant(mock.Object);
}

At this point you can start defining controllers and views and considering other front side issues leaving the database and repository definition for later. This allows the inverting of the more normal construction of the database and backend first, with the UI areas being developed second. Instead with a small amount of infrastructure in place, issues of UI design and actual data requirements of screens can be explored earlier.

I have used mock objects in tests, but it never occurred to me to use them as temporary filler in applications. This is a thought provoking technique in the sample code.

Using Model Binder To Access Session Data

This section of the sample code creates a model binder to allow access in the controller of data stored in the session. This has a twofold benefit, first it simplifies the controller code. Second it simplifies the testing of those controller methods.

The example application implements a shopping cart object that is stored in session. To access this shopping cart object a model binder is created. The effect is to decouple controller from session, since the controller accesses the cart via a parameter instead of directly through the session object of the http context.

First, starting with the Cart definition, the details are not important for our discussion here.

public class Cart
{
    ...
}

Next a model binder is defined .

public class CartModelBinder : IModelBinder
{
    private const string sessionKey = "Cart";

    public object BindModel(ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        var cart = (Cart)controllerContext.HttpContext.Session[sessionKey];
        if (cart == null)
            {
                cart = new Cart();
                controllerContext.HttpContext.Session[sessionKey] = cart;
            }
        return cart;
    }
}

Register this new binder in Global.asx.

    protected void Application_Start()
    {
        ...
        ModelBinders.Binders.Add(typeof(Cart), new CartModelBinder());
        ...
    }

 

Following is an example of a controller method that accesses the cart. Notice no references to http context and session.

[HttpPost]
public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails)
{
    if (cart.Lines.Count() == 0)
    {
        ModelState.AddModelError("", "Sorry, your cart is empty!");
    }
    if (ModelState.IsValid)
    {
        processor.ProcessOrder(cart, shippingDetails);
        cart.Clear();
        return View("Completed");
    }
    return View(shippingDetails);
}

 

The implications for testability are great. For example, the following test.

[Test]
public void CannotCheckoutEmptyCart()
{
    var mock = new Mock<IOrderProcessor>();
    var cart = new Cart();
    var shippingDetails = new ShippingDetails();
    var controller = new CartController(null, mock.Object);

    var result = controller.Checkout(cart, shippingDetails);

    mock.Verify(m => m.ProcessOrder(It.IsAny<Cart>(), It.IsAny<ShippingDetails>()),
        Times.Never());
    Assert.AreEqual("", result.ViewName); //default view name
    Assert.AreEqual(false, result.ViewData.ModelState.IsValid);
}

 

Gone is the need to stub out the http context and session. I don’t want to even imagine the mocking setup that would be required to perform the above test if the Controller access the Cart object directly in Session. This is a big simplification.

Summary

Pro ASP.NET MVC 3 Framework is informative in the broad sense about the many aspects of MVC 3. I think the quality of the sample is indicative of the quality of the book. And in the details of that sample are many points of insight, two of which are discussed above.

Categories: ASP.NET Tags: , ,