AngularJS & Kendo UI using Angular Kendo with ASP.NET MVC 5, Web API & OData

Update: 9/12/2014 – Codeplex source code download URL update.

Update: 5/1/2014 – Customer View & customerController.js has been updated to toggle styles by manipulating the DOM through the ViewModel with ng-show to adhere and stay consistent with AngularJS best practices.

Update: 5/14/2014 – Added section “Animating our View Swapping”, to elaborate on adding animation to the “ng-view” swapping process.

Source code: http://goo.gl/f1esAf
Live demo: http://longle.azurewebsites.net

With AngularJS and ASP.NET MVC, we now have an MVC pattern and architecture for both on the client and server. What do we do and/or how do we approach this? Well one can argue we don’t use any of the MVC architecture on the server and build out a full-fledged AngularJS front-end application and only use make async calls to Web API for all things that absolutely need to be on the server e.g. CRUD, workflows, business logic, etc.

Now, there’s absolutely nothing wrong with this approach, and for the most part a lot of heavy front end SPA’s are built this way in ASP.NET MVC. However, with all the .NET, ASP.NET and AngularJS goodness, why not leverage the best of both worlds? Again, there’s absolutely nothing wrong with building a pure AngularJS application and only using Web API, but for this blog post we’ll go over patterns of using the best of both worlds along with integrating AngularJS with Kendo UI.

Note: for those that prefer to build a pure AngularJS front-end and only leveraging Web API on the backend (which many devs prefer), simply ignore the MVC Razor sections. If this is the case you can also opt to render raw *.html vs. *.cshtml views as well.

Let’s take a look at our Project structure for all things client-side.

2014-05-01_1-21-35

Staying true to the AngularJS seed (sample) application from the AngularJS team, we’ll also create an “app” folder under “scripts”, staying true to ASP.NET MVC, where JS scripts are supposed to reside.

Northwind.Web/Scripts/app/app.js


var northwindApp = angular.module('northwindApp', ['kendo.directives', 'ngRoute', 'ngAnimate'])
    .config(function ($routeProvider)
    {
        $routeProvider
            .when('/home',
            {
                templateUrl: '/home/home',
                controller: 'homeController'
            })
            .when('/customer',
            {
                templateUrl: '/customer',
                controller: 'customerController'
            })
            .when('/customer/edit/:id',
            {
                templateUrl: '/customer/edit',
                controller: 'customerEditController'
            })
            .when('/help',
            {
                templateUrl: '/help'
            })
            .otherwise(
            {
                redirectTo: '/home'
            });
    });

This is where our AngularJS application bootstrapping process takes place, here we’ve declared our new app “northwindApp” and we register all our routes (URL’s) for our SPA application. Note, if you’re not building a SPA, you can still leverage all the AngularJS goodness and architecture it brings (which btw, is huge), you can just omit the registering any of the routes in this step. Notice, the parameters we are passing into the module method: “northwindApp” is the name of our app, after that it’s an array of all the AngularJS modules we want injected into our application:

  • “kendo.directives” are needed so that we have full integration with AngularJS and Kendo UI, this will make more sense when we cover how AngularJS will compile Kendo UI widgets using directives.
  • “ngRoute” is needed so that we can configure our SPA routes, if your familiar with ASP.NET MVC routes, pretty much the same principles here, only difference here is routing of your application will take place on the client vs. the server. Here we are configuring each out with the “when” method by simply passing in the path in the URL and within the “when” we configure what the server URL path is to remotely load the View and which Controller to use. The “otherwise” method is simply a default route that will be used if none of the other routes are matched from what’s in the URL. You could route the default “otherwise” route to go to a custom 404 page, for our purposes we’ll just route the user to the default home page.
  • “ngAnimate” is injected here so that we can add some animation when AngularJS swaps out our Views in our SPA.

Northwind.Web/Scripts/app/controllers/homeController.js


'use strict';

northwindApp.controller('homeController',
    function ($scope) {
        $scope.title = "ASP.NET";
    });

AngularJS has the notion of scopes with in application, there is one $rootScope and multiple children $scopes (so to speak), in your application. The best .NET analogy of a scope is something like a context, so $rootScope would map to an ApplicationContext (global with Singleton like behaivor) and $scope would map to something like a (if we had one) ControlllerContext. $scope is more of a context that you use to as an adhesive to bind you View to your Controller. So from homeController.js you see that we’ve defined a property named “title” with the value “ASP.NET”, let’s take a look at our partial Home.cshtml mark-up.

Northwind.Web/Views/Home/Home.cshtml


<div class="jumbotron">
   <h1>{{title}</h1>
   <p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS, and JavaScript.</p>
   <p><a href="http://asp.net" class="btn btn-primary btn-lg">Learn more &raquo;</a></p>
</div>
<div class="row">
   <div class="col-md-4">
       <h2>Getting started</h2>
       <p>
           ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.
       </p>
       <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301870">Learn more &raquo;</a></p>
   </div>
   <div class="col-md-4">
       <h2>Get more libraries</h2>
       <p>NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.</p>
       <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301871">Learn more &raquo;</a></p>
   </div>
   <div class="col-md-4">
       <h2>Web Hosting</h2>
       <p>You can easily find a web hosting company that offers the right mix of features and price for your applications.</p>
       <p><a class="btn btn-default" href="http://go.microsoft.com/fwlink/?LinkId=301872">Learn more &raquo;</a></p>
   </div>
</div>

Notice the {{title}} on line 2, between the h1 tags, when the View (Home.cshtml) loads, AngularJS will replace the h1 html with the $scope.title value: “ASP.NET”.

http://longle.azurewebsites.net

2014-05-01_0-04-13

Now that we have a primer to AngularJS’s MVVM pattern, let’s take a look at some bit more complex, the customerController.js which will display a Kendo UI Grid with the customizations which include a toolbar with buttons all wired up with Angular Kendo directives and AngularJS MVVM.

Northwind.Web/Scripts/app/controllers/customerController.js


'use strict';

northwindApp.controller('customerController',
    function ($scope, $rootScope, $location, customerDataSource)
    {
        customerDataSource.filter({}); // reset filter on dataSource everytime view is loaded

        var onClick = function (event, delegate)
        {
            var grid = event.grid;
            var selectedRow = grid.select();
            var dataItem = grid.dataItem(selectedRow);

            if (selectedRow.length &amp;gt; 0)
                delegate(grid, selectedRow, dataItem);
            else
                alert(&amp;quot;Please select a row.&amp;quot;);
        };

        $scope.isToolbarVisible = false;

        var toggleToolbar = function() {
            $scope.isToolbarVisible = !$scope.isToolbarVisible;
        }

        $scope.toolbarTemplate = kendo.template($(&amp;quot;#toolbar&amp;quot;).html());

        $scope.save = function (e)
        {
            onClick(e, function (grid)
            {
                grid.saveRow();
                toggleToolbar();
            });
        };

        $scope.cancel = function (e)
        {
            onClick(e, function (grid)
            {
                grid.cancelRow();
                toggleToolbar();
            });
        },

        $scope.details = function (e)
        {
            onClick(e, function (grid, row, dataItem)
            {
                $location.url('/customer/edit/' + dataItem.CustomerID);
            });
        },

        $scope.edit = function (e)
        {
            onClick(e, function (grid, row)
            {
                grid.editRow(row);
                toggleToolbar();
            });
        },

        $scope.destroy = function (e)
        {
            onClick(e, function (grid, row, dataItem)
            {
                grid.dataSource.remove(dataItem);
                grid.dataSource.sync();
            });
        },

        $scope.onChange = function (e)
        {
            var grid = e.sender;

            $rootScope.lastSelectedDataItem = grid.dataItem(grid.select());
        },

        $scope.dataSource = customerDataSource;

        $scope.onDataBound = function (e)
        {
            // check if there was a row that was selected
            if ($rootScope.lastSelectedDataItem == null)
                return;

            var view = this.dataSource.view(); // get all the rows

            for (var i = 0; i &amp;lt; view.length; i++)
            {
                // iterate through rows
                if (view[i].CustomerID == $rootScope.lastSelectedDataItem.CustomerID)
                {
                    // find row with the lastSelectedProductd
                    var grid = e.sender; // get the grid

                    grid.select(grid.table.find(&amp;quot;tr[data-uid='&amp;quot; + view[i].uid + &amp;quot;']&amp;quot;)); // set the selected row
                    break;
                }
            }
        };
    });

We declare a Controller by using the “controller” method, the first parameter is the name of our Controller, the second parameter takes a function who’s parameters are all the modules we want our Controller to be injected with. Our Controller is being injected with the following.

  • $scope, which we already know about
  • $rootScope, so that we can store some things to help us manage our View state, in this case we will store what was the last selected row on our Grid so that when the user navigates back to this view we can re-select the same row and highlight it.
  • $location, so that we can navigate from this View to other Views e.g. customer edit view which will have a form so we can make changes to a Customer.
  • customerDataSource, which is a reusable AngularJS service which returns a Kendo UI DataSource.

Notice how we are leveraging MVVM to manipulate the DOM to toggle the tool bar above the grid with the “ng-show” directive vs. manipulating the DOM with jQuery, this is key with AngularJS design principles.

As for the rest of the code in CustomerController.js it’s fairly straight forward, for me details on what each of these methods responsibilities are please have a quick read up here: http://blog.longle.net/2013/06/18/mvc-4-web-api-odata-entity-framework-kendo-ui-grid-datasource-with-mvvm.

Northwind.Web/Scripts/app/services/customerDataSource.js


'use strict';

northwindApp.factory('customerDataSource',
    function (customerModel)
    {
        var crudServiceBaseUrl = "/odata/Customer";

        return new kendo.data.DataSource({
            type: "odata",
            transport: {
                read: {
                    async: false,
                    url: crudServiceBaseUrl,
                    dataType: "json"
                },
                update: {
                    url: function (data)
                    {
                        return crudServiceBaseUrl + "(" + data.CustomerID + ")";
                    },
                    type: "put",
                    dataType: "json"
                },
                destroy: {
                    url: function (data)
                    {
                        return crudServiceBaseUrl + "(" + data.CustomerID + ")";
                    },
                    dataType: "json"
                }
            },
            batch: false,
            serverPaging: true,
            serverSorting: true,
            serverFiltering: true,
            pageSize: 10,
            schema: {
                data: function (data) { return data.value; },
                total: function (data) { return data["odata.count"]; },
                model: customerModel
            },
            error: function (e)
            {
                alert(e.xhr.responseText);
            }
        });
    });

We create the customerDataSource as a reusable AngularJS service using the “factory” method. The first parameter is the name of our service and the second parameter is method who’s parameters indicates that we are injected with customModel.

Northwind.Web/Scripts/app/services/customerModel.js


'use strict';

northwindApp.factory('customerModel', function ()
{
    return kendo.data.Model.define({
        id: "CustomerID",
        fields: {
            CustomerID: { type: "string", editable: false, nullable: false },
            CompanyName: { title: "Company", type: "string" },
            ContactName: { title: "Contact", type: "string" },
            ContactTitle: { title: "Title", type: "string" },
            Address: { type: "string" },
            City: { type: "string" },
            PostalCode: { type: "string" },
            Country: { type: "string" },
            Phone: { type: "string" },
            Fax: { type: "string" },
            State: { type: "string" }
        }
    });
});

Again, we create a reusable AngularJS service which returns a Kendo UI Customer model. This reusable Cusomer model will be used for our Grid and Controllers.

Northwind.Web/Customer/Index.cshtml


@{
   ViewBag.Title = "Index";
}

<div>
   <h2 ng-cloak>{{title}}</h2>
   <div>
       <div class="demo-section">
           <div class="k-content" style="width: 100%">
               <div kendo-grid="grid"
                    k-sortable="true"
                    k-pageable="true"
                    k-filterable="true"
                    k-editable="'inline'"
                    k-selectable="true"
                    k-toolbar='[ { template: toolbarTemplate } ]'
                    k-columns='[
                       { field: "CustomerID", title: "ID", width: "75px" },
                       { field: "CompanyName", title: "Company"},
                       { field: "ContactName", title: "Contact" },
                       { field: "ContactTitle", title: "Title" },
                       { field: "Address" },
                       { field: "City" },
                       { field: "PostalCode" },
                       { field: "Country" },
                       { field: "Phone" },
                       { field: "Fax" }]'
                    k-data-source="dataSource"
                    k-on-data-bound="onDataBound(kendoEvent)"
                    k-on-change="onChange(kendoEvent)">
               </div>
               <style scoped>
                   .toolbar { padding: 15px; float: right; }
               </style>
           </div>
       </div>

       <script type="text/x-kendo-template" id="toolbar">
           <div>
               <div class="toolbar">
                   <button kendo-button ng-click="edit(this)"><span class="k-icon k-i-tick"></span>Edit</button>
                   <button kendo-button ng-click="destroy(this)"><span class="k-icon k-i-tick"></span>Delete</button>
                   <button kendo-button ng-click="details(this)"><span class="k-icon k-i-tick"></span>Edit Details</button>
               </div>
               <div class="toolbar" style="display:none">
                   <button kendo-button ng-click="save(this)"><span class="k-icon k-i-tick"></span>Save</button>
                   <button kendo-button ng-click="cancel(this)"><span class="k-icon k-i-tick"></span>Cancel</button>
               </div>
           </div>
       </script>
   </div>
</div>

Here we can see that we that the declarative syntax for Kendo UI widgets have changed a bit, and at a first glance it seems that it’s dramatic change, but as they say, there’s a method to the madness. If you understand the convention here, it’s pretty simple, take look at the cheat sheet we have below for our view.

Customer.cshtml Cheat Sheet

Before (with Kendo UI) After (with Angular Kendo)
data-role=”grid” kendo-grid
data-sortable k-sortable
data-bind=”source: dataSource” k-data-source=”dataSource”
data-bind=”events: { change: onChange }” k-on-change=”onChange(kendoEvent)”

For declarative widgets, we simply prefix with “kendo-“, quick note, where the name has camel casing, you would simply separate it with dashes e.g. DropDownList -> k-drop-down-list.

  • Options for a widget are simply prefixed with a “k-“.
  • For all things that were bound with “data-bind”, simply become first class citizen attributes again following the dash-separted convention.
  • Same for events, however the only other item, is that you have to explicitly pass in a “kendoEvent” vs. before you didn’t.

If we run our application, we can see that everything renders and it’s business as usual.

http://longle.azurewebsites.net/#/customer

2014-05-01_0-44-42

Northwind.Web/Scripts/app/controllers/customerEditController.js


'use strict';

northwindApp.controller('customerEditController',
    function ($scope, $routeParams, $location, customerDataSource)
    {
        var customerId = $routeParams.id;

        customerDataSource.filter({ field: "CustomerID", operator: "eq", value: customerId });
        $scope.customer = customerDataSource.at(0);

        $scope.save = function ()
        {
            customerDataSource.view()[0].dirty = true;
            customerDataSource.sync();
            $location.url('/customer');
        };

        $scope.cancel = function ()
        {
            $location.url('/customer');
        }
    });

For the customerEditController.js, we are being injected with $scope, $routeParams, $location and customerDataSource, notice the reusability of our customerDataSource. We use the customerDataSource to hydrate our grid with a list of customers on our Northwind.Web.Views/Conrollers/Index.cshml view as well as our Northwind.Web/Views/Customers/Edit.cshtml view with a single customer.

On the Sever-Side of Things

In the beginning of the post, we mentioned leveraging the best of both worlds, we’ve seen how we done this on the client side of things with all the goodness of AngularJS and Kendo UI. AngularJS brings a nice fully fledged framework which forces us to using nice well defined patterns (MVC’ish & MVVM), which has a natural side effect of having an elegant architecture on the client side. So how do we make the most of ASP.NET MVC on the server side? Simple, we follow all the best practices as we’ve always been doing in the past.

Taking full advantage of MVC’s bundling and Minification

Northwind.Web/App_Start/BundleConfig.cs


using System.Web.Optimization;

namespace Northwind.Web
{
    public class BundleConfig
    {
        // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
        public static void RegisterBundles(BundleCollection bundles)
        {
            //Scripts
            bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-{version}.js",
                        "~/Scripts/jquery-migrate-{version}.js"));

            // Use the development version of Modernizr to develop with and learn from. Then, when you're ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
            bundles.Add(new ScriptBundle("~/bundles/modernizr").Include("~/Scripts/modernizr-*"));

            bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                      "~/Scripts/bootstrap.js",
                      "~/Scripts/respond.js"));

            bundles.Add(new ScriptBundle("~/bundles/angular").Include(
                      "~/Scripts/angular.js",
                      "~/Scripts/angular-route.js",
                      "~/Scripts/angular-animate.js"));

            bundles.Add(new ScriptBundle("~/bundles/kendo").Include(
                "~/Scripts/kendo/2014.1.318/kendo.web.min.js",
                "~/Scripts/angular-kendo.js"));

            bundles.Add(new ScriptBundle("~/bundles/app").Include(
                      "~/Scripts/app/app.js",
                      "~/Scripts/app/services/customerModel.js",
                      "~/Scripts/app/services/customerDataSource.js",
                      "~/Scripts/app/controllers/homeController.js",
                      "~/Scripts/app/controllers/customerController.js",
                      "~/Scripts/app/controllers/customerEditController.js"));

            //Styles
            bundles.Add(new StyleBundle("~/Content/css").Include(
                      "~/Content/bootstrap.css",
                      "~/Content/site.css"));

            bundles.Add(new StyleBundle("~/Content/kendo").Include(
                      "~/Content/kendo/2014.1.318/kendo.common.min.css",
                      "~/Content/kendo/2014.1.318/kendo.bootstrap.min.css"));

            // Set EnableOptimizations to false for debugging. For more information, visit http://go.microsoft.com/fwlink/?LinkId=301862
            BundleTable.EnableOptimizations = false;
        }
    }
}


ASP.NET’s Layout.cshtml have always been great to work with, so we stick with it.


<!DOCTYPE html>
<html lang="en" ng-app="northwindApp">
<head>
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width" />
   <meta name="description" content="SPA Application" />

   <title>@ViewBag.Title</title>

   @Styles.Render("~/Content/css")
   @Styles.Render("~/Content/kendo")
   @RenderSection("Styles", required: false)
   @Scripts.Render("~/bundles/modernizr")
</head>
<body>
   <div class="navbar navbar-inverse navbar-fixed-top">
       <div class="container">
           <div class="navbar-header">
               <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                   <span class="icon-bar"></span>
                   <span class="icon-bar"></span>
                   <span class="icon-bar"></span>
               </button>
               <a href="#/home" class="navbar-brand">http://blog.longle.net</a>
           </div>
           <div class="navbar-collapse collapse">
               <ul class="nav navbar-nav">
                   <li><a href="#/home">Home</a></li>
                   <li><a href="#/customer">Customer</a></li>
                   <li><a href="#/help">API</a></li>
               </ul>
           </div>
       </div>
   </div>
   <div class="container body-content">
       @RenderBody()
       <footer>
           <p>Copyright &copy; @DateTime.Now.Year - SPA Application</p>
       </footer>
   </div>

   @Scripts.Render("~/bundles/jquery")
   @Scripts.Render("~/bundles/angular")
   @Scripts.Render("~/bundles/app")
   @Scripts.Render("~/bundles/kendo")
   @Scripts.Render("~/bundles/bootstrap")

   @RenderSection("Scripts", required: false)
</body>
</html>

Notice we are taking advantage of all of our bundling and minification.

By default, ASP.NET MVC loads Index.cshtml e.g. http://localhost:2569/Home, again, same here, we use Index.cshtml as our landing page as usual.

Northwind.Web/Vviews/Home/Index.cshml


@{
   ViewBag.Title = "SPA";
   ViewBag.Description = "SPA App";
}
<div ng-view></div>

Notice how we have very little mark-up here and this View is simply for us to server up all the HTML in our Northwind.Web/Views/Shared/_Layout.cshml, then the @RenderBody() will render Northwind.Web/Views/Home/Index.cshml which will have the “div” element where AngularJS will load our AngularJS Views into on the client side.

Northwind.Web/Controllers/CustomerController.cs


using System.Web.Mvc;

namespace Northwind.Web.Controllers
{
    public class CustomerController : Controller
    {
        // GET: Customer
        public ActionResult Index()
        {
            return PartialView();
        }

        public ActionResult Edit()
        {
            return PartialView();
        }
    }
}

In CustomerController.cs, we are returning PartialView’s vs. View’s, well because that’s what they really are now “PartialViews”, there simply chunks of HTML that AngularJS will remotely load into our div> element when swapping views.

Northwind.Web.Api.CustomerController.cs


public class CustomerController : ODataController
{
private readonly ICustomerService _customerService;
private readonly IUnitOfWorkAsync _unitOfWorkAsync;

public CustomerController(
    IUnitOfWorkAsync unitOfWorkAsync,
    ICustomerService customerService)
{
    _unitOfWorkAsync = unitOfWorkAsync;
    _customerService = customerService;
}

// GET: odata/Customers
[HttpGet]
[Queryable]
public IQueryable<Customer> GetCustomer()
{
    return _customerService.ODataQueryable();
}

// GET: odata/Customers(5)
[Queryable]
public SingleResult<Customer> GetCustomer([FromODataUri] string key)
{
    return SingleResult.Create(_customerService.ODataQueryable().Where(t => t.CustomerID == key));
}

// PUT: odata/Customers(5)
public async Task<IHttpActionResult> Put(string key, Customer customer)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (key != customer.CustomerID)
    {
        return BadRequest();
    }

    customer.ObjectState = ObjectState.Modified;
    _customerService.Update(customer);

    try
    {
        await _unitOfWorkAsync.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!CustomerExists(key))
        {
            return NotFound();
        }
        throw;
    }

    return Updated(customer);
}

// POST: odata/Customers
public async Task<IHttpActionResult> Post(Customer customer)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    customer.ObjectState = ObjectState.Added;
    _customerService.Insert(customer);

    try
    {
        await _unitOfWorkAsync.SaveChangesAsync();
    }
    catch (DbUpdateException)
    {
        if (CustomerExists(customer.CustomerID))
        {
            return Conflict();
        }
        throw;
    }

    return Created(customer);
}

//// PATCH: odata/Customers(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] string key, Delta<Customer> patch)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    Customer customer = await _customerService.FindAsync(key);

    if (customer == null)
    {
        return NotFound();
    }

    patch.Patch(customer);
    customer.ObjectState = ObjectState.Modified;

    try
    {
        await _unitOfWorkAsync.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!CustomerExists(key))
        {
            return NotFound();
        }
        throw;
    }

    return Updated(customer);
}

// DELETE: odata/Customers(5)
public async Task<IHttpActionResult> Delete(string key)
{
    Customer customer = await _customerService.FindAsync(key);

    if (customer == null)
    {
        return NotFound();
    }

    customer.ObjectState = ObjectState.Deleted;

    _customerService.Delete(customer);
    await _unitOfWorkAsync.SaveChangesAsync();

    return StatusCode(HttpStatusCode.NoContent);
}

// GET: odata/Customers(5)/CustomerDemographics
[Queryable]
public IQueryable<CustomerDemographic> GetCustomerDemographics([FromODataUri] string key)
{
    return
        _customerService.ODataQueryable()
            .Where(m => m.CustomerID == key)
            .SelectMany(m => m.CustomerDemographics);
}

// GET: odata/Customers(5)/Orders
[Queryable]
public IQueryable<Order> GetOrders([FromODataUri] string key)
{
    return _customerService.ODataQueryable().Where(m => m.CustomerID == key).SelectMany(m => m.Orders);
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        _unitOfWorkAsync.Dispose();
    }
    base.Dispose(disposing);
}

private bool CustomerExists(string key)
{
    return _customerService.Query(e => e.CustomerID == key).Select().Any();
}
}

This is fairly straight forward, and we used the Visual Studio 2013 Update 2 RC out of the box Controller scaffolding to generate most of this code. We then replaced the code that was directly using Entity Framework to use a Repository and Service pattern, for more information on the pattern used please have a quick read here: https://genericunitofworkandrepositories.codeplex.com.

Visual Studio 2013 with Update 2 RC Web API OData Scaffolding Template

2014-05-01_1-10-42

Animating our View Swapping

To polish our UX (user experience) a bit, were going to add some animation to how AngularJS swaps view in our “ng-view” div.

Northwind.Web/Content/Site.css


body {
    padding-top: 50px;
    padding-bottom: 20px;
}

/* Set padding to keep content from hitting the edges */
.body-content {
    padding-left: 15px;
    padding-right: 15px;
    position: relative;
}

/* Set width on the form input elements since they're 100% wide by default */
input,
select,
textarea {
    max-width: 280px;
}

[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
    display: none !important;
}

.ng-enter, .ng-leave {
    -webkit-transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) .5s;
    transition: all cubic-bezier(0.250, 0.460, 0.450, 0.940) .5s;
    display: block;
    width: 1170px;
    padding-left: 15px;
    padding-right: 15px;
    position: absolute;
    left: 0;
    right: 0;
}

.ng-enter {
    left: 100%;
}

    .ng-enter.ng-enter-active {
        left: 0;
    }

.ng-leave.ng-leave-active {
    left: -200%;
}

We’ve added some rudimentary animation with .ng-enter and .ng-leave, both which the cubic-brezier, to slide in our Views. This implementation was originally based off AngularJS original animation documentation for “ng-view”, please have a quick read up here: https://docs.angularjs.org/api/ngRoute/directive/ngView for more details.

There you have it AngularJS, Kendo UI with Angular Kendo and ASP.NET MVC 5, all harmoniously with the best of both worlds.

Happy Coding…! :)

Source code: http://goo.gl/f1esAf
Live demo: http://longle.azurewebsites.net

Stay tuned for integrating this with RequireJS for large scale apps!

Upgrading to Async with Entity Framework, Web Api, OData AsyncEntitySetController, Kendo UI, Glimpse & Generic Unit of Work Repository Framework v2.0

Update [11/18/2013]: Added mocked DbContext and DbSet and example Unit Tests using the mocks, download v2.1 https://genericunitofworkandrepositories.codeplex.com.

Thanks to everyone for allowing us to give back to the .NET community, we released Generic Unit of Work and Repository Framework v1.0 for four weeks and received 655 downloads and 4121 views. This post will also serve as the documentation for release v2.0. Thanks to Ivan (@ifarkas) for helping out on the Async development, Ken for debugging the Unit of Work life cycle management for use in web applications with DI & IoC (specifically with Entlib Unity v3.0) and scaling the framework to handle Bounded DbContexts, and to the Glimpse Team (@nickswan ) for helping out on getting Glimpse MVC4 working with MVC5, and providing guidance on how to leverage Glimpse EF6 to view SQL queries from EF.

This will be part six of a six part series of blog posts.

  1. Modern Web Application Layered High Level Architecture with SPA, MVC, Web API, EF, Kendo UI, OData
  2. Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs
  3. MVC 4, Kendo UI, SPA with Layout, View, Router & MVVM
  4. MVC 4, Web API, OData, EF, Kendo UI, Grid, Datasource (CRUD) with MVVM
  5. MVC 4, Web API, OData, EF, Kendo UI, Binding a Form to Datasource (CRUD) with MVVM
  6. Upgrading to Async with Entity Framework, MVC, OData AsyncEntitySetController, Kendo UI, Glimpse & Generic Unit of Work Repository Framework v2.0

We’ll continue on from the most recent post in this series, you can do a quick review of it here http://blog.longle.net/2013/06/19/mvc-4-web-api-odata-ef-kendo-ui-binding-a-form-to-datasource-crud-with-mvvm-part. Now let’s get right into it, by first taking a look at what was all involved on the server side.

First off let’s take a quick look and the changes we made to our DbContextBase to support Async.

Repository.DbContextBase.cs

Before


    public class DbContextBase : DbContext, IDbContext
    {
        private readonly Guid _instanceId;

        public DbContextBase(string nameOrConnectionString) : base(nameOrConnectionString)
        {
            _instanceId = Guid.NewGuid();
        }

        public Guid InstanceId
        {
            get { return _instanceId; }
        }

        public void ApplyStateChanges()
        {
            foreach (var dbEntityEntry in ChangeTracker.Entries())
            {
                var entityState = dbEntityEntry.Entity as IObjectState;
                if (entityState == null)
                    throw new InvalidCastException("All entites must implement the IObjectState interface, " +
                                                   "this interface must be implemented so each entites state can explicitely determined when updating graphs.");

                dbEntityEntry.State = StateHelper.ConvertState(entityState.State);
            }
        }

        public new IDbSet<T> Set<T>() where T : class
        {
            return base.Set<T>();
        }

        protected override void OnModelCreating(DbModelBuilder builder)
        {
            builder.Conventions.Remove<PluralizingTableNameConvention>();
            base.OnModelCreating(builder);
        }

        public override int SaveChanges()
        {
            ApplyStateChanges();
            return base.SaveChanges();
        }
    }

After:


    public class DbContextBase : DbContext, IDbContext
    {
        private readonly Guid _instanceId;

        public DbContextBase(string nameOrConnectionString) : base(nameOrConnectionString)
        {
            _instanceId = Guid.NewGuid();
        }

        public Guid InstanceId
        {
            get { return _instanceId; }
        }

        public void ApplyStateChanges()
        {
            foreach (DbEntityEntry dbEntityEntry in ChangeTracker.Entries())
            {
                var entityState = dbEntityEntry.Entity as IObjectState;
                if (entityState == null)
                    throw new InvalidCastException("All entites must implement the IObjectState interface, " +
                                                   "this interface must be implemented so each entites state can explicitely determined when updating graphs.");

                dbEntityEntry.State = StateHelper.ConvertState(entityState.State);
            }
        }

        public new IDbSet<T> Set<T>() where T : class
        {
            return base.Set<T>();
        }

        public override int SaveChanges()
        {
            ApplyStateChanges();
            return base.SaveChanges();
        }

        public override Task<int> SaveChangesAsync()
        {
            ApplyStateChanges();
            return base.SaveChangesAsync();
        }

        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken)
        {
            ApplyStateChanges();
            return base.SaveChangesAsync(cancellationToken);
        }

        protected override void OnModelCreating(DbModelBuilder builder)
        {
            builder.Conventions.Remove<PluralizingTableNameConvention>();
            base.OnModelCreating(builder);
        }
    }

All that was needed here was to expose all the DbContext Async save operations so that we could use with our IUnitOfWork implementation, and also not forgetting to invoke our ApplyStateChanges so that we are managing the different states each entity could have when dealing with graphs.

Next up, are the enhancements made to our Repository.cs, so that our generic repositories can leverage the Async goodness as well.

Repostiory.Repository.cs

Before:


 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private readonly Guid _instanceId;
        internal IDbContext Context;
        internal IDbSet<TEntity> DbSet;

        public Repository(IDbContext context)
        {
            Context = context;
            DbSet = context.Set<TEntity>();
            _instanceId = Guid.NewGuid();
        }

        public Guid InstanceId
        {
            get { return _instanceId; }
        }

        public virtual TEntity FindById(object id)
        {
            return DbSet.Find(id);
        }

        public virtual void InsertGraph(TEntity entity)
        {
            DbSet.Add(entity);
        }

        public virtual void Update(TEntity entity)
        {
            DbSet.Attach(entity);
        }

        public virtual void Delete(object id)
        {
            var entity = DbSet.Find(id);
            ((IObjectState) entity).State = ObjectState.Deleted;
            Delete(entity);
        }

        public virtual void Delete(TEntity entity)
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
        }

        public virtual void Insert(TEntity entity)
        {
            DbSet.Attach(entity);
        }

        public virtual IRepositoryQuery<TEntity> Query()
        {
            var repositoryGetFluentHelper = new RepositoryQuery<TEntity>(this);
            return repositoryGetFluentHelper;
        }

        internal IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            List<Expression<Func<TEntity, object>>> includeProperties = null,
            int? page = null,
            int? pageSize = null)
        {
            IQueryable<TEntity> query = DbSet;

            if (includeProperties != null)
                includeProperties.ForEach(i => query = query.Include(i));

            if (filter != null)
                query = query.Where(filter);

            if (orderBy != null)
                query = orderBy(query);

            if (page != null && pageSize != null)
                query = query
                    .Skip((page.Value - 1)*pageSize.Value)
                    .Take(pageSize.Value);

            var results = query;

            return results;
        }
    }

After:


    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private readonly Guid _instanceId;
        private readonly DbSet<TEntity> _dbSet;

        public Repository(IDbContext context)
        {
            _dbSet = context.Set<TEntity>();
            _instanceId = Guid.NewGuid();
        }

        public Guid InstanceId
        {
            get { return _instanceId; }
        }

        public virtual TEntity Find(params object[] keyValues)
        {
            return _dbSet.Find(keyValues);
        }

        public virtual async Task<TEntity> FindAsync(params object[] keyValues)
        {
            return await _dbSet.FindAsync(keyValues);
        }

        public virtual async Task<TEntity> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
        {
            return await _dbSet.FindAsync(cancellationToken, keyValues);
        }

        public virtual IQueryable<TEntity> SqlQuery(string query, params object[] parameters)
        {
            return _dbSet.SqlQuery(query, parameters).AsQueryable();
        }

        public virtual void InsertGraph(TEntity entity)
        {
            _dbSet.Add(entity);
        }

        public virtual void Update(TEntity entity)
        {
            _dbSet.Attach(entity);
            ((IObjectState)entity).State = ObjectState.Modified;
        }

        public virtual void Delete(object id)
        {
            var entity = _dbSet.Find(id);
            Delete(entity);
        }

        public virtual void Delete(TEntity entity)
        {
            _dbSet.Attach(entity);
            ((IObjectState)entity).State = ObjectState.Deleted;
            _dbSet.Remove(entity);
        }

        public virtual void Insert(TEntity entity)
        {
            _dbSet.Attach(entity);
            ((IObjectState)entity).State = ObjectState.Added;
        }

        public virtual IRepositoryQuery<TEntity> Query()
        {
            var repositoryGetFluentHelper = new RepositoryQuery<TEntity>(this);
            return repositoryGetFluentHelper;
        }

        internal IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            List<Expression<Func<TEntity, object>>> includeProperties = null,
            int? page = null,
            int? pageSize = null)
        {
            IQueryable<TEntity> query = _dbSet;

            if (includeProperties != null)
            {
                includeProperties.ForEach(i => query = query.Include(i));
            }

            if (filter != null)
            {
                query = query.Where(filter);
            }

            if (orderBy != null)
            {
                query = orderBy(query);
            }

            if (page != null && pageSize != null)
            {
                query = query
                    .Skip((page.Value - 1)*pageSize.Value)
                    .Take(pageSize.Value);
            }
            return query;
        }

        internal async Task<IEnumerable<TEntity>> GetAsync(
                    Expression<Func<TEntity, bool>> filter = null,
                    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
                    List<Expression<Func<TEntity, object>>> includeProperties = null,
                    int? page = null,
                    int? pageSize = null)
        {
            return Get(filter, orderBy, includeProperties, page, pageSize).AsEnumerable();
        }
    }

Here we’ve exposed the FindAsync methods from DbSet, so our Repositories can make use of them, and we’ve also wrapped implemented an Async implementation of our Get() method so that we can use it in our new Web Api ProductController.cs later.

Important note: here is that although our method is named GetAsync, it is not truly performing an Async interaction, this is due to the fact that if we were to use ToListAsync(), we would already executed the the query prior to OData applying it’s criteria to the execution plan e.g. if the OData query was requesting 10 records for page 2 of a grid from a Products table that had 1000 rows in it, ToListAsync() would have actually pulled a 1000 records from SQL to the web server and at that time do a skip 10 and take 20 from the collection of Products with 1000 objects. What we want is for this to happen on the SQL Server, meaning, SQL query the Products table, skip the first 10, and take next 10 records and only send those 10 records over to the web server, which will eventually surface into the Grid in the user’s browsers. Hence we are favoring payload size (true SQL Server side paging) going over the wire, vs. a true Async call to SQL.

Northwind.Web.Areas.Spa.Api.ProductController.cs

Before:


    public class ProductController : EntitySetController<Product, int>
    {
        private readonly IUnitOfWork _unitOfWork;

        public ProductController(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        public override IQueryable<Product> Get()
        {
            return _unitOfWork.Repository<Product>().Query().Get();
        }

        protected override Product GetEntityByKey(int key)
        {
            return _unitOfWork.Repository<Product>().FindById(key);
        }

        protected override Product UpdateEntity(int key, Product update)
        {
            update.State = ObjectState.Modified;
            _unitOfWork.Repository<Product>().Update(update);
            _unitOfWork.Save();

            return update;
        }

        public override void Delete([FromODataUri] int key)
        {
            _unitOfWork.Repository<Product>().Delete(key);
            _unitOfWork.Save();
        }

        protected override void Dispose(bool disposing)
        {
            _unitOfWork.Dispose();
            base.Dispose(disposing);
        }
    }
 

After:

Note: Don’t be overwhelmed by how much more code there is in the “After” for our new ProductController that now inherits AsyncEntitySetController. I’ll explain later, what all the other Actions are there for. For now, please keep in mind there are only a few of these Actions that are actually required for the use case on the live demo site. The only Actions (methods) that are needed for our use case are as follows:

  • Task<IEnumerable> Get()
  • Task Get([FromODataUri] int key)
  • Task UpdateEntityAsync(int key, Product update)
  • Task Delete([FromODataUri] int key)

[ODataNullValue]
public class ProductController : AsyncEntitySetController<Product, int>
{
    private readonly IUnitOfWork _unitOfWork;

    public ProductController(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    protected override void Dispose(bool disposing)
    {
        _unitOfWork.Dispose();
        base.Dispose(disposing);
    }

    protected override int GetKey(Product entity)
    {
        return entity.ProductID;
    }

[Queryable]
public override async Task<IEnumerable<Product>> Get()
{
return await _unitOfWork.Repository<Product>().Query().GetAsync();
}

    [Queryable]
    public override async Task<HttpResponseMessage> Get([FromODataUri] int key)
    {
        var query = _unitOfWork.Repository<Product>().Query().Filter(x => x.ProductID == key).Get();
        return Request.CreateResponse(HttpStatusCode.OK, query);
    }

    ///// <summary>
    ///// Retrieve an entity by key from the entity set.
    ///// </summary>
    ///// <param name="key">The entity key of the entity to retrieve.</param>
    ///// <returns>A Task that contains the retrieved entity when it completes, or null if an entity with the specified entity key cannot be found in the entity set.</returns>
    [Queryable]
    protected override async Task<Product> GetEntityByKeyAsync(int key)
    {
        return await _unitOfWork.Repository<Product>().FindAsync(key);
    }

    protected override async Task<Product> CreateEntityAsync(Product entity)
    {
        if (entity == null)
            throw new HttpResponseException(HttpStatusCode.BadRequest);
            
        _unitOfWork.Repository<Product>().Insert(entity);
        await _unitOfWork.SaveAsync();
        return entity;
    }

    protected override async Task<Product> UpdateEntityAsync(int key, Product update)
    {
        if (update == null)
            throw new HttpResponseException(HttpStatusCode.BadRequest);
            
        if (key != update.ProductID)
            throw new HttpResponseException(Request.CreateODataErrorResponse(HttpStatusCode.BadRequest, new ODataError { Message = "The supplied key and the Product being updated do not match." }));

        try
        {
            update.State = ObjectState.Modified;
            _unitOfWork.Repository<Product>().Update(update);
            var x = await _unitOfWork.SaveAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
        return update;
    }

    // PATCH <controller>(key)
    /// <summary>
    /// Apply a partial update to an existing entity in the entity set.
    /// </summary>
    /// <param name="key">The entity key of the entity to update.</param>
    /// <param name="patch">The patch representing the partial update.</param>
    /// <returns>A Task that contains the updated entity when it completes.</returns>
    protected override async Task<Product> PatchEntityAsync(int key, Delta<Product> patch)
    {
        if (patch == null)
            throw new HttpResponseException(HttpStatusCode.BadRequest);

        if (key != patch.GetEntity().ProductID)
            throw Request.EntityNotFound();

        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();

        try
        {
            patch.Patch(entity);
            await _unitOfWork.SaveAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            throw new HttpResponseException(HttpStatusCode.Conflict);
        }
        return entity;
    }

    public override async Task Delete([FromODataUri] int key)
    {
        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();

        _unitOfWork.Repository<Product>().Delete(entity);

        try
        {
            await _unitOfWork.SaveAsync();
        }
        catch (Exception e)
        { 
            throw new HttpResponseException(
                new HttpResponseMessage(HttpStatusCode.Conflict)
                {
                    StatusCode = HttpStatusCode.Conflict, 
                    Content = new StringContent(e.Message), 
                    ReasonPhrase = e.InnerException.InnerException.Message
                });
        }
    }

    #region Links
    // Create a relation from Product to Category or Supplier, by creating a $link entity.
    // POST <controller>(key)/$links/Category
    // POST <controller>(key)/$links/Supplier
    /// <summary>
    /// Handle POST and PUT requests that attempt to create a link between two entities.
    /// </summary>
    /// <param name="key">The key of the entity with the navigation property.</param>
    /// <param name="navigationProperty">The name of the navigation property.</param>
    /// <param name="link">The URI of the entity to link.</param>
    /// <returns>A Task that completes when the link has been successfully created.</returns>
    [AcceptVerbs("POST", "PUT")]
    public override async Task CreateLink([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
    {
        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();
            
        switch (navigationProperty)
        {
            case "Category":
                var categoryKey = Request.GetKeyValue<int>(link);
                var category = await _unitOfWork.Repository<Category>().FindAsync(categoryKey);

                if (category == null)
                    throw Request.EntityNotFound();
                    
                    entity.Category = category;
                break;

            case "Supplier":
                var supplierKey = Request.GetKeyValue<int>(link);
                var supplier = await _unitOfWork.Repository<Supplier>().FindAsync(supplierKey);

                if (supplier == null)
                    throw Request.EntityNotFound();
                    
                    entity.Supplier = supplier;
                break;

            default:
                await base.CreateLink(key, navigationProperty, link);
                break;
        }
        await _unitOfWork.SaveAsync();
    }

    // Remove a relation, by deleting a $link entity
    // DELETE <controller>(key)/$links/Category
    // DELETE <controller>(key)/$links/Supplier
    /// <summary>
    /// Handle DELETE requests that attempt to break a relationship between two entities.
    /// </summary>
    /// <param name="key">The key of the entity with the navigation property.</param>
    /// <param name="relatedKey">The key of the related entity.</param>
    /// <param name="navigationProperty">The name of the navigation property.</param>
    /// <returns>Task.</returns>
    public override async Task DeleteLink([FromODataUri] int key, string relatedKey, string navigationProperty)
    {
        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();

        switch (navigationProperty)
        {
            case "Category":
                entity.Category = null;
                break;

            case "Supplier":
                entity.Supplier = null;
                break;

            default:
                await base.DeleteLink(key, relatedKey, navigationProperty);
                break;
        }

        await _unitOfWork.SaveAsync();
    }

    // Remove a relation, by deleting a $link entity
    // DELETE <controller>(key)/$links/Category
    // DELETE <controller>(key)/$links/Supplier
    /// <summary>
    /// Handle DELETE requests that attempt to break a relationship between two entities.
    /// </summary>
    /// <param name="key">The key of the entity with the navigation property.</param>
    /// <param name="navigationProperty">The name of the navigation property.</param>
    /// <param name="link">The URI of the entity to remove from the navigation property.</param>
    /// <returns>Task.</returns>
    public override async Task DeleteLink([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
    {
        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();

        switch (navigationProperty)
        {
            case "Category":
                entity.Category = null;
                break;

            case "Supplier":
                entity.Supplier = null;
                break;

            default:
                await base.DeleteLink(key, navigationProperty, link);
                break;
        }

        await _unitOfWork.SaveAsync();
    }
    #endregion Links

    public override async Task<HttpResponseMessage> HandleUnmappedRequest(ODataPath odataPath)
    {
        //TODO: add logic and proper return values
        return Request.CreateResponse(HttpStatusCode.NoContent, odataPath);
    }

    #region Navigation Properties
    public async Task<Category> GetCategory(int key)
    {
        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();
            
        return entity.Category;
    }

    public async Task<Supplier> GetSupplier(int key)
    {
        var entity = await _unitOfWork.Repository<Product>().FindAsync(key);

        if (entity == null)
            throw Request.EntityNotFound();
            
        return entity.Supplier;
    }
    #endregion Navigation Properties
}

Quickly looking at this, one can realize there is a lot more code than our pre-Async implementation. Well don’t be alarmed, there’s a lot of code here that wasn’t required to support our use case in the live demo (http://longle.azurewebsites.net), however we wanted to take the extra step so that we can really grasp on how to work with entity graphs with OData by leveraging the ?$expand query string parameter.

We’ll leave all the other that Actions that aren’t actually required for our use case on the live demo SPA as is, so we can see how to deep load your entity graph with OData and Web Api. We’ve included some pre-baked clickable OData URL’s (queries) on the View so that you can actually click and see the response payload in your browser (you’ll have to use Chrome or Firefox, IE has some catching up to do here).

*Click on image
10-9-2013 8-51-43 PM

Now let’s do a deep dive on the our Async Get() Action in our Controller.


[Queryable]
 
public override async Task<IEnumerable<Product>> Get()
{
    return await _unitOfWork.Repository<Product>().Query().GetAsync();
}
 

My initial thought when seeing this this Action (signature) is that it’s not IQueryable?! Which means that the SQL plan from EF has already been executed before OData has an opportunity to apply it’s criteria to the query plan! Well that’s not the case, we outfitted the Project with Glimpse and Glimpse EF6 to actually see what SQL queries were being sent over the wire.

So let’s take a look at the loading up our Kendo UI Grid with the awesomeness of Glimpse running. Since our View is built with Kendo UI, and we know it’s invoking Ajax calls to request data, we’ll click on the Ajax panel on the Glimpse HUD.

*Click on image
10-9-2013 7-30-56 PM

Now with the HUD automatically switching to standard view we can see all the Ajax requests that our View made, we are interested in the OData request that was made to hydrate our Kendo Grid.

*Click on image
10-9-2013 8-04-32 PM

After clicking on Inspect for the Ajax OData request, we see that menu buttons buttons that have tracing data for that request start to actual blink…! One of them being SQL, so let’s click on it.

*Click on image
10-9-2013 8-32-10 PM

Ladies and gentlemen, I kid you not, behold this is the actual SQL query that was from our Unit Of Work -> Repostiory -> Entity Framework 6 -> T-SQL, that was actually sent to SQL Server (actually in our case SQL Server CE, so that the live demo can be complete free with Azure Website without the need to pay for SQL Azure). BTW, we just scratching the surface of what Glimpse can do, the list is pretty much endless e.g. displays MVC Routes, Actions, Tracing, Environment Variables, MVC Views, and performance metrics for pretty much all of them, etc.

Now back to the topic at hand, we can definitively see that although our Action and our Repository are returning IEnumerable:

Get Action the Kendo UI Datasource is calling, which returns IEnumerable.


[Queryable] 
 
public override async Task<IEnumerable<Product>> Get()
{
    return await _unitOfWork.Repository<Product>().Query().GetAsync();
}
 

Repository method the Action is calling, which also returns IEnumerable.


        internal async Task<IEnumerable<TEntity>> GetAsync(
                    Expression<Func<TEntity, bool>> filter = null,
                    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
                    List<Expression<Func<TEntity, object>>> includeProperties = null,
                    int? page = null,
                    int? pageSize = null)
        {
            return Get(filter, orderBy, includeProperties, page, pageSize).AsEnumerable();
        }
 

The query plan is still valid, meaning it’s selecting only the rows (10 records to be exact) that the Grid is requesting for page one (1) of the Grid. So how is this happening? Well we’ve decorated our action with the [Queryable] attribute, so OData and Web Api is able to perform it’s magic together during run-time in the ASP.NET HTTP pipeline.

T-SQL that’s being sent over the wire, courtesy of Glimpse EF6

 
 
SELECT TOP (10 /* @p__linq__0 */) 
    [Extent1].[Product ID] AS [Product ID], 
    [Extent1].[Product Name] AS [Product Name], 
    [Extent1].[Supplier ID] AS [Supplier ID], 
    [Extent1].[Category ID] AS [Category ID], 
    [Extent1].[Quantity Per Unit] AS [Quantity Per Unit], 
    [Extent1].[Unit Price] AS [Unit Price], 
    [Extent1].[Units In Stock] AS [Units In Stock], 
    [Extent1].[Units On Order] AS [Units On Order], 
    [Extent1].[Reorder Level] AS [Reorder Level], 
    [Extent1].[Discontinued] AS [Discontinued]
    FROM [Products] AS [Extent1]
    ORDER BY [Extent1].[Product ID] ASC
 

Now, let’s cover at a high-level on all the Actions that aren’t required for our live demo use case, which are mostly to support Navigation Properties e.g. Product.Supplier, Product.Category, etc.

The $expand query string parameter allows us to hydrate complex navigation property types. For example in our case when we query for a Product, and Product has a property of Category and we the Category to be hydrated with its data we would leverage the $expand querystring parameter to do this, click this Url : http://longle.azurewebsites.net/odata/Product/?$inlinecount=allpages&$orderby=ProductName&$skip=1&$top=2&$expand=Category&$select=ProductID,ProductName,Category/CategoryID,Category/CategoryName to see the $expand in action.

T-SQL that’s being sent over the wire, again, courtesy of Glimpse EF6

 
 
SELECT TOP (2 /* @p__linq__1 */) 
    [top].[Product ID] AS [Product ID], 
    [top].[C1] AS [C1], 
    [top].[C2] AS [C2], 
    [top].[Product Name] AS [Product Name], 
    [top].[C3] AS [C3], 
    [top].[C4] AS [C4], 
    [top].[C5] AS [C5], 
    [top].[C6] AS [C6], 
    [top].[Category Name] AS [Category Name], 
    [top].[C7] AS [C7], 
    [top].[Category ID] AS [Category ID], 
    [top].[C8] AS [C8]
    FROM ( SELECT [Project1].[Product ID] AS [Product ID], [Project1].[Product Name] AS [Product Name], [Project1].[Category ID] AS [Category ID], [Project1].[Category Name] AS [Category Name], [Project1].[C1] AS [C1], [Project1].[C2] AS [C2], [Project1].[C3] AS [C3], [Project1].[C4] AS [C4], [Project1].[C5] AS [C5], [Project1].[C6] AS [C6], [Project1].[C7] AS [C7], [Project1].[C8] AS [C8]
        FROM ( SELECT 
            [Extent1].[Product ID] AS [Product ID], 
            [Extent1].[Product Name] AS [Product Name], 
            [Extent1].[Category ID] AS [Category ID], 
            [Extent2].[Category Name] AS [Category Name], 
            N'ace5ad31-e3e9-4cde-9bb8-d75fced846fa' AS [C1], 
            N'ProductName' AS [C2], 
            N'ProductID' AS [C3], 
            N'Category' AS [C4], 
            N'ace5ad31-e3e9-4cde-9bb8-d75fced846fa' AS [C5], 
            N'CategoryName' AS [C6], 
            N'CategoryID' AS [C7], 
            CASE WHEN ([Extent1].[Category ID] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C8]
            FROM  [Products] AS [Extent1]
            LEFT OUTER JOIN [Categories] AS [Extent2] ON [Extent1].[Category ID] = [Extent2].[Category ID]
        )  AS [Project1]
        ORDER BY [Project1].[Product Name] ASC, [Project1].[Product ID] ASC
        OFFSET 1 /* @p__linq__0 */ ROWS 
    )  AS [top]
 

Product results with Categories hydrated

 
  
{
  "odata.metadata":"http://longle.azurewebsites.net/odata/$metadata#Product&$select=ProductID,ProductName,Category/CategoryID,Category/CategoryName","odata.count":"77","value":[
    {
      "Category":{
        "CategoryID":2,"CategoryName":"Condiments"
      },"ProductID":3,"ProductName":"Aniseed Syrup"
    },{
      "Category":{
        "CategoryID":8,"CategoryName":"Seafood"
      },"ProductID":40,"ProductName":"Boston Crab Meat"
    }
  ]
} 
 

We can really see the power of Web Api and OData now, we’re actually able to query for Products (skip the first and take the next two) and request that Category be hydrated but specifically only the CategoryId and Name and none of the other fields.

Sample Application Client Side (Kendo UI) Tweaks

We’ve polished the UI/UX a bit, relocated Edit, Edit Details, and Delete buttons out of the rows into the Grid Toolbar (header) to make better use of the Grid real estate, using Kendo’s Template Framework, which illustrates how flexible Kendo UI can be. The app has been upgraded to, Twitter Bootstrap as by leveraging the new out of the box MVC Project Templates in Visual Studio 2013 (Preview) and changing the Kendo UI theme to Bootstrap to match.

All Kendo Views which are remotely loaded on demand into the SPA are now actually MVC Razor Views, the Kendo Router remotely loads views by traditional MVC routes e.g.
{controller}/{action}/{id} vs. what was in the previous post (http://blog.longle.net/2013/06/17/mvc-4-kendo-ui-spa-with-layout-router-mvvm/) which was just serving up raw *.html pages. This has been a request for devs that are making the transition from server side MVC development into the SPA realm, and had .NET libraries they still wanted to make use of and leverage in their their Controllers, and Razor Views for SPA’s. Obviously, all Views and ViewModel binding on the client-side are done with with Kendo’s MVVM Framework.

Northwind.Web/Areas/Spa/Content/Views/products.html

Before (non Razor, just plain *.html pages were used for SPA):

 
@{
    ViewBag.Title = "Products";
    Layout = "";
}
<div class="row">
    <div class="span5">
        <h2>Technlogy Stack</h2>
        <h3><a href="http://blog.longle.net">blog.longle.net</a></h3>
        <p>ASP.NET MVC 4, Web API, OData, Entity Framework 6 CTP, EntityFramework CE 6 RC1, Visual Studio 2013 Preview, Sql Server CE, Twitter Bootstrap, Kendo UI Web, Azure Website PaaS (<a href="http://www.windowsazure.com/en-us/develop/net/aspnet/" target="blank">free!</a>)</p>
        <br />
        <p><a class="btn" href="http://go.microsoft.com/fwlink/?LinkId=301865">Learn more &raquo;</a></p>
    </div>
</div>

<br /><br />
<div class="k-content" style="width: 100%">
    <div id="view">
        <div id="productGrid"
             data-role="grid"
             data-sortable="true"
             data-pageable="true"
             data-filterable="true"
             data-bind="source: dataSource, events: { dataBound: dataBound, change: onChange }"
             data-editable="inline"
             data-selectable="true"
             data-toolbar='[ { template: $("#toolbar").html() } ]'
             data-columns='[
                    { field: "ProductID", title: "ID", width: "50px" },
                    { field: "ProductName", title: "Name"},
                    { field: "QuantityPerUnit", title: "Quantity", width: "200px" },
                    { field: "UnitsInStock", title: "Stock", width: "90px" },
                    { field: "UnitPrice", title: "Price", format: "{0:c}", width: "100px" },
                    { field: "Discontinued", width: "150px" } ]'>
        </div>
    </div>
    <h3>Use Chrome or Firefox and click on OData (queries) Urls below for example results.</h3>
    <ul>
        <li><a href="/odata/$metadata">/odata/$metadata</a></li>
        <li><a href="/odata/Product">/odata/Product</a></li>
        <li><a href="/odata/Product/?$select=ProductID,ProductName">/odata/Product/?$select=ProductID,ProductName</a></li>
        <li><a href="/odata/Product/?$orderby=ProductName&$skip=1&$top=2">/odata/Product/?$orderby=ProductName&$skip=1&$top=2</a></li>
        <li><a href="/odata/Product/?$orderby=ProductName&$skip=1&$top=2">/odata/Product/?$orderby=ProductName&$skip=1&$top=2</a></li>
        <li><a href="/odata/Product/?$inlinecount=allpages&$filter=UnitPrice ge 20">/odata/Product/?$inlinecount=allpages&$filter=UnitPrice ge 20</a></li>
        <li><a href="/odata/Product/?$expand=Category">/odata/Product/?$expand=Category</a></li>
        <li><a href="/odata/Product/?$expand=Category&$select=ProductID,ProductName,Category/CategoryID,Category/CategoryName">/odata/Product/?$expand=Category&$select=ProductID,ProductName,Category/CategoryID,Category/CategoryName</a></li>
        <li><a href="/odata/Product/?$inlinecount=allpages&$orderby=ProductName&$skip=1&$top=2&$expand=Category&$select=ProductID,ProductName,Category/CategoryID,Category/CategoryName">/odata/Product/?$inlinecount=allpages&$orderby=ProductName&$skip=1&$top=2&$expand=Category&$select=ProductID,ProductName,Category/CategoryID,Category/CategoryName</a></li>
    </ul>
</div>

<script type="text/x-kendo-template" id="toolbar">
    <div class="toolbar">
        <a class="k-button" onclick="edit(event);"><span class="k-icon k-i-tick"></span>Edit</a>
        <a class="k-button" onclick="destroy(event);"><span class="k-icon k-i-tick"></span>Delete</a>
        <a class="k-button" onclick="details(event);"><span class="k-icon k-i-tick"></span>Edit Details</a>
    </div>
    <div class="toolbar" style="display:none">
        <a class="k-button" onclick="save(event);"><span class="k-icon k-i-tick"></span>Save</a>
        <a class="k-button" onclick="cancel(event);"><span class="k-icon k-i-tick"></span>Cancel</a>
    </div>
</script>

<script>
    var lastSelectedDataItem;

    var save = function (event) {
        onClick(event, function (grid) {
            grid.saveRow();
            $(".toolbar").toggle();
        });
    };

    var cancel = function (event) {
        onClick(event, function (grid) {
            grid.cancelRow();
            $(".toolbar").toggle();
        });
    };

    var details = function (event) {
        onClick(event, function (grid, row, dataItem) {
            window.location.href = '#/edit/' + dataItem.ProductID;
        });
    };

    var edit = function (event) {
        onClick(event, function (grid, row) {
            grid.editRow(row);
            $(".toolbar").toggle();
        });
    };

    var destroy = function (event) {
        onClick(event, function (grid, row, dataItem) {
            grid.dataSource.remove(dataItem);
            grid.dataSource.sync();
        });
    };

    var onClick = function (event, delegate) {
        event.preventDefault();
        var grid = $("#productGrid").data("kendoGrid");
        var selectedRow = grid.select();
        var dataItem = grid.dataItem(selectedRow);
        if (selectedRow.length > 0)
            delegate(grid, selectedRow, dataItem);
        else
            alert("Please select a row.");
    };

    var Product = kendo.data.Model.define({
        id: "ProductID",
        fields: {
            ProductID: { type: "number", editable: false, nullable: true },
            ProductName: { type: "string", validation: { required: true } },
            QuantityPerUnit: { type: "string", validation: { required: true } },
            UnitsInStock: { type: "number", validation: { required: true } },
            UnitPrice: { type: "number", validation: { required: true, min: 1 } },
            Discontinued: { type: "boolean" }
        }
    });

    var baseUrl = "/odata/Product";

    var dataSource = new kendo.data.DataSource({
        type: "odata",
        transport: {
            read: {
                url: baseUrl,
                dataType: "json"
            },
            update: {
                url: function (data) {
                    return baseUrl + "(" + data.ProductID + ")";
                },
                dataType: "json"
            },
            destroy: {
                url: function (data) {
                    return baseUrl + "(" + data.ProductID + ")";
                },
                dataType: "json"
            }
        },
        batch: false,
        serverPaging: true,
        serverSorting: true,
        serverFiltering: true,
        pageSize: 10,
        schema: {
            data: function (data) {
                return data.value;
            },
            total: function (data) {
                return data["odata.count"];
            },
            errors: function (e) {
                return e.errors;
            },
            model: Product
        },
        error: function (e) {
            var responseJson = e.xhr.responseJSON;
            if (responseJson != undefined) {
                if (responseJson["odata.error"] != undefined) {
                    var error = responseJson["odata.error"];
                    var message = error.message.value + '\n\n' + error.innererror.message;
                    alert(message);
                }
            } else {
                alert(e.xhr.status + "\n\n" + e.xhr.responseText + "\n\n" + e.xhr.statusText);
            }
            this.read();
        }
    });

    var viewModel = kendo.observable({
        dataSource: dataSource,
        dataBound: function (arg) {
            if (lastSelectedDataItem == null) return; // check if there was a row that was selected
            var view = this.dataSource.view(); // get all the rows
            for (var i = 0; i < view.length; i++) { // iterate through rows
                if (view[i].ProductID == lastSelectedDataItem.ProductID) { // find row with the lastSelectedProductd
                    var grid = arg.sender; // get the grid
                    grid.select(grid.table.find("tr[data-uid='" + view[i].uid + "']")); // set the selected row
                    break;
                }
            }
        },
        onChange: function (arg) {
            var grid = arg.sender;
            lastSelectedDataItem = grid.dataItem(grid.select());
        },
    });

    $(document).bind("viewSwtichedEvent", function (e, args) { // subscribe to the viewSwitchedEvent
        if (args.name == "list") { // check if this view was switched too
            if (args.isRemotelyLoaded) { // check if this view was loaded for the first time (remotely from server)
                kendo.bind($("#view"), viewModel); // bind the view to the model
            } else {// view already been loaded in cache
                viewModel.dataSource.read(); // refresh grid
            }
        }
    });

</script>
<style scoped>
    #productGrid .k-toolbar {
        padding: .7em;
    }

    .toolbar {
        float: right;
    }
</style>
 

10-9-2013 7-11-39 PM

Happy Coding…! :)

Live Demo: http://longle.azurewebsites.net
Download: https://genericunitofworkandrepositories.codeplex.com/

Modern Web Application Layered High Level Architecture with SPA, MVC, Web API, EF, Kendo UI, OData

This will be part one of a six part series of blog posts.

  1. Modern Web Application Layered High Level Architecture with SPA, MVC, Web API, EF, Kendo UI, OData
  2. Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs
  3. MVC 4, Kendo UI, SPA with Layout, View, Router & MVVM
  4. MVC 4, Web API, OData, EF, Kendo UI, Grid, Datasource (CRUD) with MVVM
  5. MVC 4, Web API, OData, EF, Kendo UI, Binding a Form to Datasource (CRUD) with MVVM
  6. Upgrading to Async with Entity Framework, MVC, OData AsyncEntitySetController, Kendo UI, Glimpse & Generic Unit of Work Repository Framework v2.0

Update: 07/22/2013 – Added blog series on actual implementation steps, for this architecture with patterns towards the end of this blog post.

Search and searched and seems difficult to locate any comprehensive top down, full stack architecture or high level design diagrams for modern (SPA) web apps. It’s probably important you have at least a high level picture what this architecture looks like now that there is quite a bit more design work involved on the client side especially with more and more implementations are around SPA and patterns like MVVM; so hence this post. Obviously there is no such thing as one size fits all especially when it comes to architecture, so feel free to omit or add to the architecture based on your specific needs.

Modern Web Application Logical and Physical Architecture High Level Design with SPA

Client Layer (HTML5 Browser)
Model View ViewModel (MVVM) is a design pattern which helps developers separate the Model (the data) from the View (the UI). The View-Model part of MVVM is responsible for exposing the data objects from the Model in such a way that those objects are easily consumed in the View. Kendo MVVM is an implementation of the MVVM pattern which seamlessly integrates with the rest of the Kendo framework (widgets and DataSource).

Web Layer (Server)
Almost the entire ASP.NET MVC Web Layer can leverage the DI & IoC Pattern, you can read up on what the benefits are and how to do this download both a sample MVC app that uses MEF or Unity 3 from one of my previous post.

  • Presentation Layer
    For modern MVC web applications, the presentation layer (server-side) consists of a Controllers who’s only tasks are to render an HTML page, css, Javascript, HTML templates, images, etc. Very little server-side code, if any, is responsible for any UI rendering responsibilities. Once the page is rendered in the browser client-side components (the browser or user agent that executes scripts and displays the HTML). With client-side techniques such as AJAX and with rich client-side frameworks such as Kendo UI Web, it is possible to execute logic on the client, for nice fluid user experiences. Implementing a SPA, can greatly increase the user experience by, reducing or eliminating post backs and refreshes.

  • Business Layer
    When designing the business layer for your Web application, consider how to implement the business logic and long-running workflows. Using a separate business layer that implements the business logic and workflows can improve the maintainability and testability of your application, and allow you to centralize and reuse common business logic functions.

  • Data Layer
    Consider designing a data layer for your Web application that abstracts the logic necessary to access the database. This can be achieved with implementing the Repository pattern, the Repository pattern is often implemented with the Unit of Work pattern. Entity Framework already implements the Unit of Work Pattern with the DbContext, however you should always work an abstraction of this, you can read up on one of previous post on how to do this. Using a separate data layer makes the application easier to configure and maintain, and hides the details of the database from other layers of the application.

    Your business entities, usually shared between the layers of your application e.g. Business and Data Layer should be POCO entities. Entity Framework enables you to use custom data classes together with your data model without making any modifications to the data classes themselves. This means that you can use “plain-old” CLR objects (POCO), such as existing domain objects, with your data model. These POCO data classes (also known as persistence-ignorant objects), which are mapped to entities that are defined in a data model, support most of the same query, insert, update, and delete behaviors as entity types that are generated by the Entity Data Model tools.

Services Layer
Consider designing a separate service layer if you plan to deploy your business layer on a remote tier, or if you plan to expose your business logic using a Web service. Design the services to achieve maximum reusability by not assuming the specific details of clients that will use them, and avoid changes over time that might break the service interface for existing clients. Instead, implement versions of the interface to allow clients to connect to the appropriate version.

Download PNG Version
Download PDF Version

I’ve posted a three part blog series, that covers the actual implementation of most of this architecture and patterns used:

Part 1 – MVC 4, Kendo UI, SPA with Layout, View, Router & MVVM
Part 2 – MVC 4, Web API, OData, EF, Kendo UI, Grid, Datasource (CRUD) with MVVM
Part 3 – MVC 4, Web API, OData, EF, Kendo UI, Binding a Form to Datasource (CRUD) with MVVM

Happy Architecting…!

Using LinqPad to Query & Troubleshoot Your Custom Entity Framework DbContext, Unit of Work & Repository Pattern

I received a couple of questions on how to use LinqPad to run queries through the Unit of Work and Repository pattern implementations from my previous blog. With that being said, here’s a blog post on how to accomplish this since I wasn’t able to find any good documentation on this either.

For anyone that has not had the opportunity to work with LinqPad, this is definite a must have, as part of your development toolkit. It’s great to hash out some quick code, PoC’s, and troubleshooting EF queries. LinqPad is awesome, especially when working with a large solution, so that you can avoid lengthy build times and compilations just to test out a small block of code. You can quickly and easily test and verify your code and/or queries first in LinqPad, before building it into your actual project.

Download the source code (https://skydrive.live.com/redir?resid=949A1C97C2A17906!5536&authkey=!APbwDGYDnBjHJPE) from my previous blog that uses EF5 and make sure everything compiles.

Note: Although in the original blog post I used the Alpha release of EF6, I’ve ran into some intermittent issues with it using LinqPad, so for purposes of this post we’ll stick the release version, EF5.

Launch Linqpad and perform the following steps:

  1. Add a new Connection
    6-11-2013 5-01-17 PM

  2. Add a Typed Data Context Connection
    6-11-2013 5-03-33 PM

  3. Browse to the Data.dll in the Web\Bin directory
    6-11-2013 5-07-31 PM

  4. Browse to the Web.config file of the Web project
    6-11-2013 5-09-26 PM

  5. Verify that the Northwind DbContext has been reflected on by LinqPad
    6-11-2013 5-10-21 PM

  6. Navigate to Query Properties dialog window
    6-11-2013 5-12-01 PM
  7. Add all the following references so that we can use the Unit of Work and Repository assemblies
    6-11-2013 5-13-48 PM

  8. NuGet Entity Framework Version 5.0.0
    6-11-2013 5-15-06 PM

  9. Verify that your Query Properties has the following references
    6-11-2013 5-16-12 PM
  10. Add the following Namespace Imports
    6-11-2013 5-17-09 PM

  11. Change your Query Language to C# Program and select NorthwindContext in Data.dll as your Connection.
    6-11-2013 5-18-59 PM
  12. Code up a LinqPad query using the Unit of Work and Repository implementation and out the results using the LinqPad’s Dump() extension method.
    
    void Main()
    {
    	int? page= null;
    	var pageNumber = page ?? 1;
    	const int pageSize = 20;
    	int totalCustomerCount;
    	var unitOfWork = new UnitOfWork();
    
    	var customers =
    		unitOfWork.Repository<Customer>()
    			.Query()
    			.Include(i => i.Orders)
    			.OrderBy(q => q
    				.OrderBy(c => c.ContactName)
    				.ThenBy(c => c.CompanyName))
    			.Filter(q => q.ContactName != null)
    			.GetPage(pageNumber, pageSize, out totalCustomerCount);	
    			
    	customers.Dump();
    }
    
    
  13. You should see your results returned through our Unit of Work and Repository implementation
    6-11-2013 5-24-15 PM

  14. As well as the generated SQL from EF
    6-11-2013 5-25-24 PM

Happy Coding…! :)

Building an Extensible Fluent Validation Framework using Generic Funcs and Wiring it up to MVC 4 with ModelValidatorProvider and ModelValidator

Live demo http://longle.azurewebsites.net/customer, courtesy of Windows Azure free 10 Websites, will keep this out there for as long as it’s free :)

We will start off in a pre-baked solution from my last post http://blog.longle.net/2013/05/17/generically-implementing-the-unit-of-work-repository-pattern-with-entity-framework-in-mvc-simplifying-entity-graphs-part-2-mvc-4-di-ioc-with-mef-attributeless-conventions/ using Unity 3.0 as our DI framework of choice.

Before we starts let’s define some of key architecture & design principles we are attempting to achieve here.

  • Validation Framework can run in any .NET technology space e.g. WPF, MVC, ASP.NET WebForms, ASP.NET MVC, SilverLight, WF, Windows Services, WCF, etc..
  • Validation Framework is extensible, and easy to do so.
  • Validation Framework will support reusable validations so that we can reuse them across the enterprise.
  • Validation Framework will support ad-hoc validations, meaning validations, that potentially will only be used once, that are not common validations, and very specific to a given entity with a unique use case.
  • Validation Framework can easily be plugged in e.g such as the MVC 4 run-time, not requiring developer’s to do anything different than they are today to validate their models.

We have a lot to cover so let’s dive right into the Validation project and its’ code!

Validation.Validator.cs


namespace Validation
{
    public class Validator<TModel> : IValidator
    {
        private readonly List<ValidationResult> _validationResults;
        private readonly List<Validation<TModel>> _validations;

        public Validator()
        {
            _validations = new List<Validation<TModel>>();
            _validationResults = new List<ValidationResult>();
        }

        public List<ValidationResult> Validate(object model)
        {
            foreach (var validation in _validations)
            {
                validation.OnValidating();
                var validater = validation.GetValidater();

                if (!validater((TModel) model))
                    _validationResults.Add(validation.GetValidationResult());
            }

            return _validationResults;
        }

        public Validation<TModel> AddValidation(Validation<TModel> validationRule)
        {
            _validations.Add(validationRule);
            return validationRule;
        }
    }
}

This is probably one of the most important class of our Validation Framework, it primary responsibility are:

  • Iterating through all of our validations for a given entity
  • Storing the result for each of the validations

Validation.Validation.cs


    public class Validation<TModel>
    {
        private string _errorMessage;
        private string _propertyName;
        private Func<TModel, bool> _validater;
        private Expression<Func<TModel, object>> _property;

        public Expression<Func<TModel, object>> GetProperty()
        {
            return _property;
        }

        public Validation<TModel> SetProperty(Expression<Func<TModel, object>> property)
        {
            _property = property;
            return this;
        }

        public string GetPropertyName()
        {
            var memberExpression = GetProperty().Body as MemberExpression ?? ((UnaryExpression) GetProperty().Body).Operand as MemberExpression;

            if (memberExpression == null)
            {
                _propertyName = default(string);
                return _propertyName;
            }

            _propertyName = memberExpression.Member.Name;

            return _propertyName;
        }

        public Validation<TModel> SetErrorMessage(string errorMessage)
        {
            _errorMessage = errorMessage;
            return this;
        }

        public Validation<TModel> SetValidater(Func<TModel, bool> validater)
        {
            _validater = validater;
            return this;
        }

        public Func<TModel, bool> GetValidater()
        {
            return _validater;
        }

        public string GetErrorMessage()
        {
            return _errorMessage;
        }

        public ValidationResult GetValidationResult()
        {
            return new ValidationResult {ErrorMessage = GetErrorMessage(), PropertyName = GetPropertyName()};
        }

        public virtual void OnValidating()
        {
            if (string.IsNullOrEmpty(_errorMessage))
                _errorMessage = GetPropertyName() + " is not valid";
        }}

The Validation class is primarily responsible for:

  • Keeping track which property of the entity it’s its validation is for.
  • Setting the appropriate validation result and message.

Now really this is really all you need to start validating your entities/models. Although, there are some other classes that are in the Validation project, they are really not needed to start validating. They are there to help us later wire up our Validation Framework to MVC 4. With that being said let’s wire up our first validation, all you need to do is implement the the Validator class.

I’ve added a UnitTest project with two test class, one is simply a dummy model with fictitious property names, the property names are named after the type of validation we are performing on the property so that we can easily understand what type of validation is happening on each of them. The second class MyValidator is where are validations actually reside for validating MyModel.cs.

Notice all the validations are stored in the MyValidator.cs class and that our MyModel.cs knows nothing about any validation business. More importantly our validation is completely decoupled from our entities, giving us nice separation of concerns.

Validation.Tests.MyModel.cs

Note: Again, The property names here are named so that we can easily see what types of validations are happening for each of them. Obviously in the real world they would be named FirstName, LastName, Age, versus Compare1, Compare2, Regex, etc. :p


namespace Validation.Test
{
    public class MyModel
    {
        public string Compare1 { get; set; }
        public string Compare2 { get; set; }
        public string CreditCard { get; set; }
        public string Id { get; set; }
        public string Length { get; set; }
        public string Range { get; set; }
        public string Regex { get; set; }
        public string Required { get; set; }
        public string IpAddress { get; set; }
        public string Email { get; set; }
    }
}

Validation.Tests.MyValidator.cs
A simple test/sample validator implementation so that we can code up a quick unit test.


namespace Validation.Test
{
    public class MyValidator : Validator<MyModel>
    {
        public MyValidator()
        {
            this.ValidateCompare(m => m.Compare1, m => m.Compare2)
                .WithDataType(ValidationDataType.Integer)
                .WithOperator(ValidationOperator.GreaterThan);

            this.ValidateCreditCard(m => m.CreditCard);

            this.ValidateId(m => m.Id);

            this.ValidateLength(m => m.Length)
                .WithMax(5)
                .WithMin(1);

            this.ValidateRange(m => m.Range)
                .WithDataType(ValidationDataType.Integer)
                .WithMin(1)
                .WithMax(5);

            this.ValidateRegex(m => m.Regex)
                .SetPattern(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")
                .SetErrorMessage("Email must be in valid format");

            this.ValidateRequired(m => m.Required);

            this.ValidateIpAddress(m => m.IpAddress);

            this.ValidateEmail(m => m.Email);

            this.AddValidation()
                .SetProperty(m => m.Email)
                .SetValidater(model => !string.IsNullOrEmpty(model.Email))
                .SetErrorMessage("Email is required");
        }
    }
}

Here we have some simple validation implemented and wired up for our MyModel.cs entity. Here we are using some of the out of the box validations I’ve coded up, and some ad-hoc validations we are able to add to our Validator that the Validation Framework provides e.g. validating and Id, length, range, using Regex, required, IP address, email, etc.. Obviously you can easily add your own reusable validation, and again add ad-hoc (lines 34-37) validations that will probably only be specific to a given entity.

Now let’s test our validation by coding up a quick unit test.

ValidationTests.UnitTest1.cs


namespace Validation.Test
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var myModel = new MyModel();
            var myValidator = new MyValidator();
            var validationResults = myValidator.Validate(myModel);
            
            Assert.AreEqual(validationResults.Count, 4);
            Assert.AreEqual(validationResults[0].PropertyName, "Compare1" );
            Assert.AreEqual(validationResults[1].PropertyName, "Id");
            Assert.AreEqual(validationResults[2].PropertyName, "Required");
            Assert.AreEqual(validationResults[3].PropertyName, "Email");
        }
    }
}

If we run our unit test, we can see that MyModel.cs was nicely validated…!

6-3-2013 2-41-04 PM

Now, this is great, but how do we seamlessly wire this up to MVC 4..?! Good question, let’s get started. To wire up our new Validation Framework with the MVC 4 run-time there are a couple of things we need to do.

First is to implement the ModelValidatorProvider (http://msdn.microsoft.com/en-us/library/system.web.mvc.modelvalidatorprovider(v=vs.98).aspx), this will provide the MVC 4 runtime the necessary implementation to get a set of validations for a model.

Validation.CustomModelValidatorProvider.cs


namespace Validation
{
    public class CustomModelValidatorProvider : ModelValidatorProvider
    {
        public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
        {
            var isPropertyValidation = metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName);

            if (isPropertyValidation) yield break;

            var validatorFactory = new ValidatorFactory();
            var validator = validatorFactory.GetValidator(metadata.ModelType, metadata.Model);

            if (validator != null)
                yield return new CustomModelValidator(metadata, context, validator);
        }
    }
}

When implementing the ModeValidatorProvider we see that we do need a factory of some sort to new up an instance of the right Validator to validate our model. We accomplish this with the ValidatorFactory. This class is responsible for discovering the ValidatorAttribute that the MVC model should be decorated with has the Validator type to be activated to validate the model.

Validation.ValidatorFactory.cs


namespace Validation
{
    public class ValidatorFactory
    {
        public IValidator GetValidator(Type type, object model)
        {
            var validatorAttributes = type.GetCustomAttributes(typeof (ValidatorAttribute), true);

            if (validatorAttributes.Length > 0)
            {
                var validatorAttribute = (ValidatorAttribute) validatorAttributes[0];
                return Activator.CreateInstance(validatorAttribute.Validator) as IValidator;
            }   
            return null;
        }
    }
}

The ValidatorAttribute is pretty straight forward, it’s an attribute that accepts a type, which is the Validator type we need to activate to validate the model.

Validation.ValidatorAttribute.cs


namespace Validation
{
    [AttributeUsage(AttributeTargets.Class)]
    public class ValidatorAttribute : Attribute
    {
        private readonly Type _validator;

        public ValidatorAttribute(Type validator)
        {
            _validator = validator;
        }


        public Type Validator
        {
            get { return _validator; }
        }
    }
}

Let’s take a look at our Customer model that is decorated with the ValidatorAttribute so that our ValidatorFactory knows which Validator to activate for it, in our case CustomerValidator.cs.

Data.Models.Customer.cs


namespace Data.Models
{
    [Validator(typeof (CustomerValidator))]
    public class Customer : IObjectState
    {
        public Customer()
        {
            Orders = new List<Order>();
            CustomerDemographics = new List<CustomerDemographic>();
        }

        public string CustomerID { get; set; }
        public string CompanyName { get; set; }
        public string ContactName { get; set; }
        public string ContactTitle { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string Region { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
        public string Phone { get; set; }
        public string Fax { get; set; }
        public virtual ICollection<Order> Orders { get; set; }
        public virtual ICollection<CustomerDemographic> CustomerDemographics { get; set; }
        public ObjectState State { get; set; }
    }
}

Second, we need to implement the ModelValidator (http://msdn.microsoft.com/en-us/library/system.web.mvc.modelvalidator(v=vs.108).aspx), this will provide the MVC 4 runtime to call into our Validation Framework and execute our Validator.Validate(object model) method and return a set of ValidationResults. Once our ValidationResult payload is returned we will then need to map it back to MVC’s ModelValidationResult so that it can display our validation messages correctly.

Validation.CustomModelValidator.cs


namespace Validation
{
    public class CustomModelValidator : ModelValidator
    {
        private readonly IValidator _validator;

        public CustomModelValidator(ModelMetadata metadata, ControllerContext controllerContext, IValidator validator)
            : base(metadata, controllerContext)
        {
            _validator = validator;
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            var validationResults = _validator.Validate(Metadata.Model);

            return validationResults
                .Select(validationResult => 
                    new ModelValidationResult
                        {
                            MemberName = validationResult.PropertyName, 
                            Message = validationResult.ErrorMessage
                        });
        }
    }
}

Finally we need to register our Validation.CustomModelValidatorProvider.cs implementation with the MVC runtime.

Web.Global.asax.cs


namespace 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()
        {
            Bootstrapper.Initialise();

            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();

            // Add our custom ModelValiidatorProvider for MVC runtime
            ModelValidatorProviders.Providers.Add(new CustomModelValidatorProvider());
        }
    }
}

Now let’s start off by validating one of our existing entities Entites.Customer.cs. You can really place your validation objects anywhere you’d like, for simplicity sake I’ll go ahead and place them in the same project as our Entities under a folder named Validations.

Entities.Validators.CustomerValidator.cs


namespace Entities.Validations
{
    internal class CustomerValidator : Validator<Customer>
    {
        public CustomerValidator()
        {
            this.ValidateId(m => m.CustomerID);

            this.ValidateLength(m => m.CompanyName)
                .WithMax(5)
                .WithMin(1);

            this.ValidateRequired(m => m.ContactName);

            this.AddValidation()
                .SetProperty(m => m.City)
                .SetValidater(model => !string.IsNullOrEmpty(model.City))
                .SetErrorMessage("Customer city is required");
        }
    }
}

Now let’s edit our existing CustomController and add two more actions for editing our Customer entity.

Web.Controllers.CustomerController.cs


namespace Web.Controllers
{
    public class CustomerController : Controller
    {
        private readonly IUnitOfWork _unitOfWork;

        public CustomerController(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        public ActionResult Index(int? page)
        {
            var pageNumber = page ?? 1;
            const int pageSize = 20;

            int totalCustomerCount;

            var customers =
                _unitOfWork.Repository<Customer>()
                    .Query()
                    .Include(i => i.Orders)
                    .OrderBy(q => q
                        .OrderBy(c => c.ContactName)
                        .ThenBy(c => c.CompanyName))
                    .Filter(q => q.ContactName != null)
                    .GetPage(pageNumber, pageSize, out totalCustomerCount);

            ViewBag.Customers = new StaticPagedList<Customer>(
                customers, pageNumber, pageSize, totalCustomerCount);

            return View();
        }

        [HttpGet]
        public ActionResult Edit(string id)
        {
            var customer = _unitOfWork.Repository<Customer>().FindById(id);
            return View(customer);
        }

        [HttpPost]
        public ActionResult Edit(Customer customer)
        {
            if (ModelState.IsValid)
                RedirectToAction("Edit");
            
            _unitOfWork.Repository<Customer>().Update(customer);
            _unitOfWork.Save();

            return View(customer);
        }
    }
}

Notice how we are calling the MVC ModelState.IsValid, and when we debugging this, we see that the MVC run-time will invoke our custom Validation Framework.

6-3-2013 3-30-50 PM

Our error message from Entities.Validation.CustomerValidator.cs.

6-3-2013 3-31-57 PM

All of the out of the box Validators that are included in the example download, follow the described pattern listed below, this is also how you would extend or add your own reusable validations to the framework.

  1. Extend the Validator, by writing an Extension method
  2. Instantiating a fluent helper class for the validation
  3. Setting the property to be validated
  4. Setting the validation logic
  5. Adding the the validation to stack of validations to the Validator instance

Let’s take a quick look at one of the out of the box validations e.g. ValidateLength.

Validation.Validators.ValidateLengthExtension.cs


namespace Validation.Validators
{
    public static class ValidateLengthExtension
    {
        public static ValidateLengthFluentHelper<TModel> ValidateLength<TModel>(this Validator<TModel> validator, Expression<Func<TModel, object>> property)
        {
            var fluentHelper = new ValidateLengthFluentHelper<TModel>();

            fluentHelper.SetProperty(property);
            fluentHelper.SetValidater(model =>
                {
                    var value = property.GetPropertyValue(model) as string;

                    return string.IsNullOrEmpty(value) || value.Length >= fluentHelper.GetMin() && value.Length <= fluentHelper.GetMax();
                });

            validator.AddValidation(fluentHelper);

            return fluentHelper;
        }
    }
}

To provide a nice and easy to use fluent API experience, let’s take a look at the fluent helper class for this validation.

Validation.Validators.ValidateLengthFluentHelper.cs


namespace Validation.Validators
{
    public class ValidateLengthFluentHelper<TModel> : Validation<TModel>
    {
        private int _max;
        private int _min;

        public override void OnValidating()
        {
            if (String.IsNullOrEmpty(GetErrorMessage()))
                SetErrorMessage(GetPropertyName() + " must be between " + _min + " and " + _max + " characters long.");
        }

        public int GetMin()
        {
            return _min;
        }

        public int GetMax()
        {
            return _max;
        }

        public new ValidateLengthFluentHelper<TModel> SetProperty(Expression<Func<TModel, object>> propertySelector)
        {
            base.SetProperty(propertySelector);
            return this;
        }

        public ValidateLengthFluentHelper<TModel> WithMin(int min)
        {
            _min = min;
            return this;
        }

        public ValidateLengthFluentHelper<TModel> WithMax(int max)
        {
            _max = max;
            return this;
        }
    }
}

To use this is fairly straight forward and simple..!

ValidationTest.MyValidator.cs


            this.ValidateLength(m => m.Length)
                .WithMax(5)
                .WithMin(1);


Support for Ad-Hoc Validations with Generic Funcs using AddValidation() Fluent Method

Finally, let’s quickly go over adding ad-hoc validations by adding in-line Lambda’s or Generic Funcs, all you hvae to do is call AddValidation() and using the fluent API, and make sure your generic func accepts a TModel (could be of any object type) and returns a Boolean. In the sample code below we are doing a simple ad-hoc validation for the property Email, validating if there’s a value or not and returning an validation message.

ValidationTest.MyValidator.cs


            this.AddValidation()
                .SetProperty(m => m.Email)
                .SetValidater(model => !string.IsNullOrEmpty(model.Email))
                .SetErrorMessage("Email is required");


There you have it, a Validation Framework registered and wired up to the MVC 4 run-time.

Happy Coding..! :)

Download sample application.

https://skydrive.live.com/redir?resid=949A1C97C2A17906!5153&authkey=!AKbcAGKuETlcm6M

Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs

Update: 02/24/2014 – v3.2 released, improved API and reusable queries with the variation of the Query Object Pattern. Breaking change: Framework now ships returning all things TEntity or IEnumberable for compartmentalization, you will need to change the Repository.cs (see below, what methods signatures to change) if IQueryable is preferred over IEnumerable, IEnumerable is preferred as a best practice (http://genericunitofworkandrepositories.codeplex.com/documentation).

Update [01/06/2014] Quick start online video: http://blog.longle.net/2014/01/06/unit-of-work-unity-3-quick-start-video/

1-3-2014 5-43-38 PM

Update [11/18/2013]: Added mocked DbContext and DbSet and example Unit Tests using the mocks, download: https://genericunitofworkandrepositories.codeplex.com

This will be part two of a six part series of blog posts.

  1. Modern Web Application Layered High Level Architecture with SPA, MVC, Web API, EF, Kendo UI, OData
  2. Generically Implementing the Unit of Work & Repository Pattern with Entity Framework in MVC & Simplifying Entity Graphs
  3. MVC 4, Kendo UI, SPA with Layout, View, Router & MVVM
  4. MVC 4, Web API, OData, EF, Kendo UI, Grid, Datasource (CRUD) with MVVM
  5. MVC 4, Web API, OData, EF, Kendo UI, Binding a Form to Datasource (CRUD) with MVVM
  6. Upgrading to Async with Entity Framework, MVC, OData AsyncEntitySetController, Kendo UI, Glimpse & Generic Unit of Work Repository Framework v2.0

Update: 09/18/2013 – Sample application and source code has been uploaded to CodePlex: https://genericunitofworkandrepositories.codeplex.com, updated project to VS2013, Twitter Bootstrap, MVC 5, EF6, Kendo UI Bootstrap theme, project redeployed to Windows Azure Website.

Update: 07/30/2013 – To see this implementation with DI & IoC with EntLib Unity v3.0, see post: Bounded DbContext with Generic Unit of Work, Generic Repositories, Entity Framework 6 & EntLib Unity 3.0 in MVC 4.

Update: 06/21/2013 – Bug fix: Issue with deleting objects by Id in Repository.Delete(object Id). Updated blog post, and sample solution and added live demo link.

Live demo: http://longle.azurewebsites.net

First off let’s elegantly setup our solution, and prep it for real world development. We have our solution broken up into four different projects, now let’s talk about the “why?”.

5-8-2013 9-08-17 PM

Data Project (Data Access Layer)

This is where all of our ORM tooling related objects reside. In our case the EF (Entity Framework 6.0 Alpha 3) DataContext, Mappings, Migrations, etc. This give is nice separation, control and isolation of where any persistence related objects live. If ever, one day we need to change the tool of choice, or even upgrade, there’s only one layer or project to do this in, the Data project.

5-8-2013 10-08-26 PM

Entities Project (Domain Layer)
The Entities project is where all of our POCO (Plan Old C# Objects) objects will live. POCO’s should be very ignorant objects that pretty much have nothing in them but the structure of your data. With that being said, typically anything outside our Repository layer e.g. presentation layer (MVC), services layer (will cover in next post) should be completely ignorant to any persistence tool or technology e.g. NHibernate, eXpressPersistent, OpenAccess, EF (our case), etc.

5-8-2013 10-09-31 PM

Repository (Layer)
This is where our UoW (Unit of Work) pattern will be implemented as well as our Repository implementation. Our UoW implementation will handle most of our usual CRUD activities.

Two important objectives we will try to with our UoW pattern implementation are:

  1. Abstract away the ORM tool, in our case EF.
  2. Ensuring that all interactions with the database are happening under one DbContext instance per page request.

Obviously there are many other benefits, such giving us the ability to implement different variations of our UoW, potentially wire up to different types of repositories. For purposes of this article, we’ll stake focus on our two primary objectives, and I’ll cover the other benefits in later posts.

Web Project (Presentation Layer)
This is our presentation layer, for the purposes of the blog, we will use MVC (ASP.NET MVC 4). Again, this project should not have any dependent code on EF assembly, therefore that should not be any references to the EF assembly, it should only reference our Repository project for data access.

Refactoring the NorthwindContext for an Abstracted and Cleaner Implementation

Now that we’ve gone over the solution and it’s projects, let’s do a little bit of refactoring and cleaning up with our EF code.

Data.NorthwindDataContext.cs

Before:


    public class NorthwindContext : DbContext
    {
        static NorthwindContext()
        {
            Database.SetInitializer<NorthwindContext>(null);
        }

        public NorthwindContext()
            : base("Name=NorthwindContext")
        {
        }

        public DbSet Category Categories { get; set; }
        public DbSet CustomerDemographic CustomerDemographics { get; set; }
        public DbSet Customer Customers { get; set; }
        public DbSet Employee Employees { get; set; }
        public DbSet OrderDetail Order_Details { get; set; }
        public DbSet Order Orders { get; set; }
        public DbSet Product Products { get; set; }
        public DbSet Region Regions { get; set; }
        public DbSet Shipper Shippers { get; set; }
        public DbSet Supplier Suppliers { get; set; }
        public DbSet Territory Territories { get; set; }
        public DbSet Invoice Invoices { get; set; }


        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CategoryMap());
            modelBuilder.Configurations.Add(new CustomerDemographicMap());
            modelBuilder.Configurations.Add(new CustomerMap());
            modelBuilder.Configurations.Add(new EmployeeMap());
            modelBuilder.Configurations.Add(new Order_DetailMap());
            modelBuilder.Configurations.Add(new OrderMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new RegionMap());
            modelBuilder.Configurations.Add(new ShipperMap());
            modelBuilder.Configurations.Add(new SupplierMap());
            modelBuilder.Configurations.Add(new TerritoryMap());
            modelBuilder.Configurations.Add(new InvoiceMap());
        }
    }

After:

     public class NorthwindContext : DbContext, IDbContext
    {
        static NorthwindContext()
        {
            Database.SetInitializer<NorthwindContext>(null);
        }

        public NorthwindContext()
            : base("Name=NorthwindContext")
        {
        }

        public new IDbSet<T> Set<T>() where T : class
        {
            return base.Set<T>();
        }

        public override int SaveChanges()
        {
            this.ApplyStateChanges();
            return base.SaveChanges();
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new CategoryMap());
            modelBuilder.Configurations.Add(new CustomerDemographicMap());
            modelBuilder.Configurations.Add(new CustomerMap());
            modelBuilder.Configurations.Add(new EmployeeMap());
            modelBuilder.Configurations.Add(new Order_DetailMap());
            modelBuilder.Configurations.Add(new OrderMap());
            modelBuilder.Configurations.Add(new ProductMap());
            modelBuilder.Configurations.Add(new RegionMap());
            modelBuilder.Configurations.Add(new ShipperMap());
            modelBuilder.Configurations.Add(new SupplierMap());
            modelBuilder.Configurations.Add(new TerritoryMap());
            modelBuilder.Configurations.Add(new InvoiceMap());
        }
    }

We can see that our DbContext is now much cleaner, and that it implements IDbContext. IDbContext will be the abstraction we will be working with when interacting with it’s concrete implementation, NorthwindContext.

Best Practice, Coding Against Abstractions or Interfaces

Abstractions serve as a nice flexibility point later, allowing us to implement different variations of the abstraction (interface). This will be very useful later when we implement DI (Dependency Injection and IoC (Inverse of Control) patterns. Coding to an abstraction will also help us easily create unit test, allowing us to inject faked or mocked instances as well. If your a bit unclear on how this helps set stage for DI, IoC and Unit Testing, no worries, I’ll cover these topics in the next post.

Data.IDbContext.cs


namespace Data
{
    public interface IDbContext
    {
        IDbSet<T> Set<T>() where T : class;
        int SaveChanges();
        DbEntityEntry Entry(object o);
        void Dispose();
    }
}

Now, let’s take take a look at what’s all in our Repository project, where our generic extensible repositories will reside.

5-10-2013 7-02-10 PM

IUnitOfWork This is simply the contract or abstraction that we will be working with, when interacting with it’s concrete implementation which will be UnitOfWork object.

Repository.IUnitOfwork.cs



namespace Repository
{
    public interface IUnitOfWork
    {
        void Dispose();
        void Save();
        void Dispose(bool disposing);
        IRepository<T> Repository<T>() where T : class;
    }
}

Concrete Implementation of IUnitOfWork.cs


namespace Repository
{
    public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbContext _context;

        private bool _disposed;
        private Hashtable _repositories;

        public UnitOfWork(IDbContext context)
        {
            _context = context;
        }

        public UnitOfWork()
        {
            _context = new NorthwindContext();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Save()
        {
            _context.SaveChanges();
        }

        public virtual void Dispose(bool disposing)
        {
            if (!_disposed)
                if (disposing)
                    _context.Dispose();

            _disposed = true;
        }

        public IRepository<T> Repository<T>() where T : class
        {
            if (_repositories == null)
                _repositories = new Hashtable();

            var type = typeof (T).Name;

            if (!_repositories.ContainsKey(type))
            {
                var repositoryType = typeof (Repository<>);

                var repositoryInstance = 
                    Activator.CreateInstance(repositoryType
                            .MakeGenericType(typeof (T)), _context);
                
                _repositories.Add(type, repositoryInstance);
            }

            return (IRepository<T>) _repositories[type];
        }
    }
}

Let’s take a look at our IRepository Repository() method here in our UnitOfWork implementation. Here we are storing all the activated instances of repositories for each and every requests. One there is a request for a given repository we will first check to see if our Hashtable (container to hold all of our activated repository instances) has been created, if not, will go ahead and create our container. Next, we’ll scan our container to see if the requested repository instance has already been created, if it has, then will return it, if it hasn’t, we will activate the requested repository instance, store it in our container, and then return it. If it helps, you can think of this as lazy loading our repository instances, meaning we are only creating repository instances on demand, this allows us to only create the repository instances needed for a given web request. Last but not least, notice here how we are following best practices mentioned earlier, we are not return the concrete implementation for the Repository, but the abstraction, IRepository.

Repository.IRepository.cs


namespace Repository
{
    public interface IRepository<TEntity> where TEntity : class
    {
        TEntity FindById(object id);
        void InsertGraph(TEntity entity);
        void Update(TEntity entity);
        void Delete(object id);
        void Delete(TEntity entity);
        void Insert(TEntity entity);
        RepositoryQuery<TEntity> Query();
    }
}


Repository.Repository.cs


    public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        internal IDbContext Context;
        internal IDbSet<TEntity> DbSet;

        public Repository(IDbContext context)
        {
            Context = context;
            DbSet = context.Set<TEntity>();
        }

        public virtual TEntity FindById(object id)
        {
            return DbSet.Find(id);
        }

        public virtual void InsertGraph(TEntity entity)
        {
            DbSet.Add(entity);
        }

        public virtual void Update(TEntity entity)
        {
            DbSet.Attach(entity);
        }

        public virtual void Delete(object id)
        {
            var entity = DbSet.Find(id);
            var objectState = entity as IObjectState;
            if (objectState != null) 
                objectState.State = ObjectState.Deleted;
            Delete(entity);
        }

        public virtual void Delete(TEntity entity)
        {
            DbSet.Attach(entity);
            DbSet.Remove(entity);
        }

        public virtual void Insert(TEntity entity)
        {
            DbSet.Attach(entity);
        }

        public virtual RepositoryQuery<TEntity> Query()
        {
            var repositoryGetFluentHelper =
                new RepositoryQuery<TEntity>(this);

            return repositoryGetFluentHelper;
        }

        internal IQueryable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>,
                IOrderedQueryable<TEntity>> orderBy = null,
            List<Expression<Func<TEntity, object>>>
                includeProperties = null,
            int? page = null,
            int? pageSize = null)
        {
            IQueryable<TEntity> query = DbSet;
            
            if (includeProperties != null)
                includeProperties.ForEach(i => { query = query.Include(i); });

            if (filter != null)
                query = query.Where(filter);

            if (orderBy != null)
                query = orderBy(query);

            if (page != null && pageSize != null)
                query = query
                    .Skip((page.Value - 1)*pageSize.Value)
                    .Take(pageSize.Value);

            return query;
        }
    }

Our generic implementation for Repository allows us to have have all our basic heavy lifting of a Repository out of the box for any one of our Entities. All we have to do is request for the Repository of interest by passing in the Entity e.g.

UnitOfWork.Repository<Customer>

will give us the Customer Repository with all our out of the box plumbing available.

Let’s take a quick look at our Get method in the Repository implementation.


        internal IEnumerable<TEntity> Get(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>,
                IOrderedQueryable<TEntity>> orderBy = null,
            List<Expression<Func<TEntity, object>>>
                includeProperties = null,
            int? page = null,
            int? pageSize = null)
        {
            IQueryable<TEntity> query = DbSet;
            
            if (includeProperties != null)
                includeProperties.ForEach(i => query.Include(i));

            if (filter != null)
                query = query.Where(filter);

            if (orderBy != null)
                query = orderBy(query);

            if (page != null && pageSize != null)
                query = query
                    .Skip((page.Value - 1)*pageSize.Value)
                    .Take(pageSize.Value);


            return query.ToList();
        }

The Get method here, handles fetching data. It handles querying the data supporting a filtering, ordering, paging, and eager loading of child types, so that we can make one round trip and eager load the entity graph.

We notice here that the method is marked “internal”, this is because we only want the Get method here to be accessible to objects with the same assembly, Repository.dll. We will expose the Get method via the Query method and return the RepositoryQuery object to provide a fluent “ish” api, so that’s its a bit more easy and intuitive for our developers when querying with our Repository layer. Note, only methods in our RepositoryQuery will actually invoke the internal Get method, again, which is why we went ahead and marked the Get method internal.

Repository.RepositoryQuery.cs (our fluent api helper class)


    public sealed class RepositoryQuery<TEntity> where TEntity : class
    {
        private readonly List<Expression<Func<TEntity, object>>> 
            _includeProperties;

        private readonly Repository<TEntity> _repository;
        private Expression<Func<TEntity, bool>> _filter;
        private Func<IQueryable<TEntity>, 
            IOrderedQueryable<TEntity>> _orderByQuerable;
        private int? _page;
        private int? _pageSize;

        public RepositoryQuery(Repository<TEntity> repository)
        {
            _repository = repository;
            _includeProperties = 
                new List<Expression<Func<TEntity, object>>>();
        }

        public RepositoryQuery<TEntity> Filter(
            Expression<Func<TEntity, bool>> filter)
        {
            _filter = filter;
            return this;
        }

        public RepositoryQuery<TEntity> OrderBy(
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy)
        {
            _orderByQuerable = orderBy;
            return this;
        }

        public RepositoryQuery<TEntity> Include(
            Expression<Func<TEntity, object>> expression)
        {
            _includeProperties.Add(expression);
            return this;
        }

        public IEnumerable<TEntity> GetPage(
            int page, int pageSize, out int totalCount)
        {
            _page = page;
            _pageSize = pageSize;
            totalCount = _repository.Get(_filter).Count();

            return _repository.Get(
                _filter, 
                _orderByQuerable, _includeProperties, _page, _pageSize);
        }

        public IEnumerable<TEntity> Get()
        {
            return _repository.Get(
                _filter, 
                _orderByQuerable, _includeProperties, _page, _pageSize);
        }
    }

Addressing IRepository<TEntity> Extensibility

Well, what happens if we need extra methods a specific Repository? Meaning, how do we address “extensiblility” in our Repository? No problem, we have a couple of options here, we can simply inherit a Repository and add your own methods to it, or what I prefer, create extension methods e.g. extending IRepository (with some pseudo code for validating an address with UPS).

Repository.CustomerRepository.cs

    /// <summary>
    /// Extending the IRepository<Customer>
    /// </summary>
    public static class CustomerRepository
    {
        public static decimal GetCustomerOrderTotalByYear(
            this IRepository<Customer> customerRepository, 
            int customerId, int year)
        {
            return customerRepository
                .FindById(customerId)
                .Orders.SelectMany(o => o.OrderDetails)
                .Select(o => o.Quantity*o.UnitPrice).Sum();
        }

    /// <summary>
    /// TODO:
    /// This should really live in the Services project (Business Layer), 
    /// however, we'll leave it here for now as an example, and migrate
    /// this in the next post.
    /// </summary>
        public static void AddCustomerWithAddressValidation(
            this IRepository<Customer> customerRepository, Customer customer)
        {
            USPSManager m = new USPSManager("YOUR_USER_ID", true);
            Address a = new Address();
            a.Address1 = customer.Address;
            a.City = customer.City;

            Address validatedAddress = m.ValidateAddress(a);

            if (validatedAddress != null)
            customerRepository.InsertGraph(customer);
        }
    }

Great, now that we have our project nicely structured with the our generic implementation of the Unit of Work and Repository Pattern, let’s see how we can leverage this by wiring up a simple controller to show a list of customers.

To help us with this go ahead and NuGet the PagedList for MVC so we easily create a view with a paged grid.

5-10-2013 6-22-37 PM

Let’s create a CustomerController Index Action load a paged list of customers to hydrate a grid.


    public class CustomerController : Controller
    {
        public ActionResult Index(int? page)
        {
            var pageNumber = page ?? 1;
            const int pageSize = 20;

            var unitOfWork = new UnitOfWork();

            int totalCustomerCount;

            var customers =
                unitOfWork.Repository<Customer>()
                    .Query()
                    .Include(i => i.CustomerDemographics)
                    .OrderBy(q => q
                        .OrderBy(c => c.ContactName)
                        .ThenBy(c => c.CompanyName))
                    .Filter(q => q.ContactName != null)
                    .GetPage(pageNumber, pageSize, out totalCustomerCount);

            ViewBag.Customers = new StaticPagedList<Customer>(
                customers, pageNumber, pageSize, totalCustomerCount);

            unitOfWork.Save();

            return View();
        }
    }

Next, let’s wire up the Index.cshtml view for our CustomerController Index Action.


@{
    ViewBag.Title = "Customers";
}

@using PagedList.Mvc;
@using PagedList;

<h2>Customers</h2>

<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />

<h2>List of Customers</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table style="width: 100%; padding: 10px;">
    <tr style="background-color: lightgray; padding: 10px;">
        <th>#</th>
        <th>Company
        </th>
        <th>Name
        </th>
        <th>Title
        </th>
        <th>Order Date
        </th>
    </tr>

    @foreach (var item in ViewBag.Customers)
    {
        <tr>
            <td>
                @Html.ActionLink(
                    "Edit", "Edit", new { id = item.CustomerID }) |

                @Html.ActionLink(
                    "Details", "Details", new { id = item.CustomerID }) |

                @Html.ActionLink(
                    "Delete", "Delete", new { id = item.CustomerID })
            </td>
            <td>
                @item.CompanyName
            </td>
            <td>
                @item.ContactName
            </td>
            <td>
                @item.ContactTitle
            </td>
            <td>
                @if (item.Orders.Count > 1)
                {
                    @item.Orders[1].OrderDate.ToShortDateString()
                }
            </td>
        </tr>
    }


    <tr>
        <td colspan="9">
            @Html.PagedListPager(
                (IPagedList)ViewBag.Customers, page => 
                    Url.Action("Index", new { page }))
        </td>
    </tr>

</table>


Go ahead and run our project to see our paged customers grid.

5-10-2013 6-46-30 PM

Abstracting the Complexity when Dealing with Entity Graphs

Another item I wanted to go over was insert and updating graphs with our Repository pattern. There are four use cases for inserting graphs, they are as follows.

5-10-2013 7-34-28 PM

To abstract the complexity and EF experience required, and how the DbContext manages graphs e.g to know to set the root entity state and how it affects other entities in the graph (e.g. updating the root entity in the graph, and existing entities in the graph are to be updated or deleted) we added a interface IOjectState that all of our entities will implement.

Entities.IObjectState.cs


namespace Data
{
    public interface IObjectState
    {
        ObjectState State { get; set; }
    }
}

Entities.ObjectState.cs (enum)


namespace Data
{
    public enum ObjectState
    {
        Unchanged,
        Added,
        Modified,
        Deleted
    }
}

These two classes will allow our development team to explicitly set the state of each of the entities in the graph when inserting or updating a graph. To make use of the classes we’ll need to extend the DbContext with a few methods, we’ll do this by creating extension methods.

Data.DbContextExtension.cs


    public static class DbContextExtension
    {
        public static void ApplyStateChanges(this DbContext dbContext)
        {
            foreach (var dbEntityEntry in dbContext.ChangeTracker.Entries())
            {
                var entityState = dbEntityEntry.Entity as IObjectState;
                if (entityState == null)
                    throw new InvalidCastException(
                        "All entites must implement " +
                        "the IObjectState interface, this interface " +
                        "must be implemented so each entites state" +
                        "can explicitely determined when updating graphs.");

                dbEntityEntry.State = ConvertState(entityState.State);
            }
        }

        private static EntityState ConvertState(ObjectState state)
        {
            switch (state)
            {
                case ObjectState.Added:
                    return EntityState.Added;
                case ObjectState.Modified:
                    return EntityState.Modified;
                case ObjectState.Deleted:
                    return EntityState.Deleted;
                default:
                    return EntityState.Unchanged;
            }
        }
    }

Now we will override the SaveChanges in our NorthwindContext to invoke the ApplyStateChanges method to synchronize our ObjectSate with EF’s EntityState, so that the context will know how to deal with each and every entity when dealing with entity graphs.

Data.NorthwindContext.SaveChanges()


        public override int SaveChanges()
        {
            this.ApplyStateChanges();
            return base.SaveChanges();
        }


Now when inserting, updating you can explicitly set the entities state, especially useful when dealing with graphs. This abstracts the skill-set of a developer using our Repository of having to know the what, when and how to set the state of entities in the graph in order for the context to update the graph and persist the graph correctly. Let’s take a look at an example of updating an existing Order and adding an OrderDetail item with an entity graph. Both these actions, are will be executed on the same graph, however notice that the action is different for both of the entity’s, one is updating and the other is adding, however we will only be invoking one method (IRepository.Update(TEntity entity) from our IRepository in one transaction.

So we’ll demonstrate and prove out updating an entity graph with our UnitOfWork implementation in these steps.

Code Snippet from LinqPad, notice how we are explicitly setting each of the entities state in the entity graph.


var unitOfWork = new UnitOfWork(this);

var orderId = 10253;

unitOfWork
	.Repository<Order>()
	.Query()
	.Include(t => t.Customer)
	.Filter(t=> t.OrderID == orderId)
	.Get().Dump();

var order = unitOfWork
	.Repository<Order>()
	.FindById(orderId);

unitOfWork
	.Repository<OrderDetail>()
	.Query()
	.Filter(t => t.OrderID == orderId)
	.Get().Dump();

order.ShipName = "Long Le";
order.State = ObjectState.Modified;

order.OrderDetails.Add(
	new OrderDetail{
		ProductID = 2,
		UnitPrice = 10,
		Quantity = 2,
		Discount = 1,
		State = ObjectState.Added
	}
);


unitOfWork.Repository<Order>()
	.Update(order);

unitOfWork.Save();

new UnitOfWork(this)
	.Repository<Order>()
	.Query()
	.Include(t => t.Customer)
	.Filter(t => t.OrderID == orderId)
	.Get().Dump();
	
new UnitOfWork(this)
	.Repository<OrderDetail>()
	.Query()
	.Filter(t => t.OrderID == orderId)
	.Get().Dump();
		

Entity Graph Update Scenario

  1. Query the Order table, make note of the ShipName value.
  2. Query the OrderDetail table, make not there are only three (3) items, that belong to the same Order.
  3. Update the ShipName value in the Order.
  4. Add an OrderDetail to the Order.

5-11-2013 4-13-23 PM

(click image to enlarge)

Presto, we were able to successfully update an existing Order and add a new OrderDetail via an entity graph with one transaction using one method. Now, we can absolutely do this using EF out of the box, however, our goal here is was to abstract the complexity and skill set required from a developer in regards to EF specially how do deal with the DbContext in order to make this happen as well as obviously still support working with graphs through our IRepository implementation.

There you have it, and extensible genericized implementation of the UoW and Repository pattern with EF in MVC. In the next blog post, we’ll add DI & IoC to this solution and introduce the Services layer, this layer will house all of our business logic and rules. We will also implement the Services layer in a way, where we don’t violate our Unit of Work pattern, meaning all the work done in our Repository and Services are executed under one single instance the DbContext per page request.

Happy Coding..! :)

Download sample: https://genericunitofworkandrepositories.codeplex.com

Multi-Step (Two-Factor) ASP.NET MVC 4 Registration with SMS using Twilio Cloud Communication and SimpleMembershipProvider for Increased User Validity

Some sites such as live.com, gmail.com will require a multi-step registration and/or forgot password workflows to validate you say you are. Having an opportunity working with the Twilio Cloud Communication Platform, exposed how easily this can be done with their Api’s.

So for this post, I wanted to illustrate the steps in getting your MVC 4 application wired up with multi-step registration process with SMS code verification leveraging Twilio. We will start from my last blog post with Seed Users and Roles with MVC 4, SimpleMembershipProvider, SimpleRoleProvider, EntityFramework 5 CodeFirst, and Custom User Properties.

Since we already gathered the user’s mobile number during registration, let’s go ahead and add a property/field “IsSmsVerified” and run EntityFramework’s migration command update-database -verbose (so we can see what commands are being issued to our database for the migration.

NuGet and install the Twilio.Mvc package.

Update our UserProfile entity with IsSmsVerified and SmsVerificationCode properties.


    [Table("UserProfile")]
    public class UserProfile
    {
        public UserProfile()
        {
            IsSmsVerified = false;
        }

        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string Mobile { get; set; }
        [DefaultValue(false)]
        public bool IsSmsVerified { get; set; }
        public string SmsVerificationCode { get; set; }
    }

Update our Seed method so that we are not inserting nulls for the provisioned users.


#region

using System.Data.Entity.Migrations;
using System.Linq;
using System.Web.Security;
using MVC4SimpleMembershipCodeFirstSeedingEF5.Models;
using WebMatrix.WebData;

#endregion

namespace MVC4SimpleMembershipCodeFirstSeedingEF5.Migrations
{
    internal sealed class Configuration : DbMigrationsConfiguration<UsersContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
        }

        protected override void Seed(UsersContext context)
        {
            WebSecurity.InitializeDatabaseConnection(
                "DefaultConnection",
                "UserProfile",
                "UserId",
                "UserName", autoCreateTables: true);

            if (!Roles.RoleExists("Administrator"))
                Roles.CreateRole("Administrator");

            if (!WebSecurity.UserExists("lelong37"))
                WebSecurity.CreateUserAndAccount(
                    "lelong37",
                    "password",
                    new {Mobile = "+19725000374", IsSmsVerified = false});

            if (!Roles.GetRolesForUser("lelong37").Contains("Administrator"))
                Roles.AddUsersToRoles(new[] {"lelong37"}, new[] {"Administrator"});
        }
    }
}

Run: update-database -verbose from the Package Manager Console

Now the fun begins, let’s update our AccountController.

  • Update the Register(RegisterModel model) Action and introduce the second step registration process of entering an SMS verfication code that we send the user using Twilio’s REST Api Client.

    Note: We are just scratching the tip of the ice berg in terms of what the Twilio Cloud Communication offers, you can visit their docs site for more info.

  • Add SmsVerification() Action, so that the user can enter the SMS verification code.
  • Add SmsVerication(SmsVerificationModel smsVerificationModel) Action, so that we can validate the user, the user’s mobile number, and SMS verification code.
  • Add GenerateSimpleSmsVerificationCode() method, a simple static helper method to generate a six character SMS verification code.

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {
                    var smsVerificationCode =
                        GenerateSimpleSmsVerificationCode();

                    WebSecurity.CreateUserAndAccount(
                        model.UserName,
                        model.Password,
                        new
                            {
                                model.Mobile,
                                IsSmsVerified = false,
                                SmsVerificationCode = smsVerificationCode
                            },
                        false);

                    var twilioRestClient = new TwilioRestClient(
                        ConfigurationManager.AppSettings.Get("Twilio:AccoundSid"),
                        ConfigurationManager.AppSettings.Get("Twilio:AuthToken"));

                    twilioRestClient.SendSmsMessage(
                        "+19722001298",
                        model.Mobile,
                        string.Format(
                            "Your ASP.NET MVC 4 with Twilio " +
                            "registration verification code is: {0}",
                            smsVerificationCode)
                        );

                    Session["registrationModel"] = model;

                    return RedirectToAction("SmsVerification", "Account");
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        [AllowAnonymous]
        public ActionResult SmsVerification()
        {
            return View(new SmsVerificationModel
                {
                    Username =
                        ((RegisterModel) Session["registrationModel"])
                            .UserName
                });
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult SmsVerification(SmsVerificationModel smsVerificationModel)
        {
            if (ModelState.IsValid)
            {
                var userContext = new UsersContext();

                var userProfile = userContext.UserProfiles
                    .Single(u => u.UserName == smsVerificationModel.Username);

                var registerModel = ((RegisterModel) Session["registrationModel"]);

                if (userProfile.SmsVerificationCode == smsVerificationModel.SmsVerificationCode)
                {
                    WebSecurity.Login(userProfile.UserName, registerModel.Password);
                    return RedirectToAction("Index", "Home");
                }
            }

            ModelState.AddModelError("", "The SMS verfication code was incorrect.");
            return RedirectToAction("SmsVerification", "Account");
        }

        private static string GenerateSimpleSmsVerificationCode()
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            var random = new Random();
            return new string(
                Enumerable.Repeat(chars, 6)
                    .Select(s => s[random.Next(s.Length)])
                    .ToArray());
        }

We could combine the two actions SmsVerication() and SmsVerication(SmsVerificationModel smsVerificationModel) into one, by checking the request verb for GET or Post, however for separation of concerns we will keep them “nice” and “separate”.

Let’s add some AppSettings entries to store our Twilio Rest Api credentials.


  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="Twilio:AccoundSid" value="youtwilioaccountid" />
    <add key="Twilio:AuthToken" value="yourtwilioauthtoken" />
  </appSettings>

Note: Your Twilio credentials for using their REST Api can be found on your dashboard after registering.

Create a SmsVerification ViewModel.


using System.ComponentModel.DataAnnotations;

using System.ComponentModel.DataAnnotations;

namespace MVC4SimpleMembershipCodeFirstSeedingEF5.Models
{
    public class SmsVerificationModel
    {
        [Display(Name = "Username")]
        public string Username { get; set; }

        [Required]
        [Display(Name = "SMS Verification Code")]
        public string SmsVerificationCode { get; set; }
    }
}

Let’s create the SmsVerification View where a user can input the SMS verification code that we sent to the user bound to the ViewModel we just created.

@model MVC4SimpleMembershipCodeFirstSeedingEF5.Models.SmsVerificationModel
@{
    //ViewBag.Title = "SMS Verification with MVC 4 & Twilio";
    ViewBag.Title = "SmsVerification";
}

<hgroup class="title">
    <h1>@ViewBag.Title.<br/></h1>
    <h3>Please enter your SMS verification code to complete registration.</h3>
</hgroup>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary()

    @Html.HiddenFor(m => m.Username)

    <fieldset>
        <legend>SMS Verifcation Form</legend>
        <ol>
            <li>
                @Html.LabelFor(m => m.Username)
                @Html.DisplayTextFor(m => m.Username) 
                <br/><br/>
            </li>
            <li>
                @Html.LabelFor(m => m.SmsVerificationCode)
                @Html.TextBoxFor(m => m.SmsVerificationCode)
            </li>
        </ol>
        <input type="submit" value="SmsVerification" />
    </fieldset>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Step 1 of the registration process, run the application and register.

For a quick sanity check let’s just make sure our SimpleMembershipProvider is persisting the extra properties we added earlier e.g. SmsVerificationCode, IsSmsVerified.


SELECT TOP 1000 [UserId]
      ,[UserName]
      ,[Mobile]
      ,[IsSmsVerified]
      ,[SmsVerificationCode]
  FROM [aspnet-MVC4SimpleMembershipCodeFirstSeedingEF5].[dbo].[UserProfile]

Good, we can see here that Mobile, IsSmsVerified and SmsVerificationCode is being saved when we invoked the WebSecurity.CreateUserAndAccount method earlier from our Registration Action.


                    WebSecurity.CreateUserAndAccount(
                        model.UserName,
                        model.Password,
                        new
                            {
                                model.Mobile,
                                IsSmsVerified = false,
                                SmsVerificationCode = smsVerificationCode
                            },
                        false);

Step 2, SMS notification to the user’s mobile number was received with the SMS verification code.

Step 3 of the registration process, input the SMS verification code in the SMSVerfication View.

You have now successfully completed the 3 step registration process and have been automatically logged into the site!

Now there are obviously TODO’s here, you can create an new authorize Attribute to verify that the IsSmsVerified property for the user is not false, clean up how we are storing the RegisterModel in session, additional bullet proofing the app in terms of security gaps, etc.. However the emphasis of this blog was multi-step registration to for increased validity of the user.

Last but not least, you can use the a similar implementation for things like forgot password or any other type of workflow that needs that extra degree of validation.

Happy Coding…! :)

Download sample application: https://skydrive.live.com/redir?resid=949A1C97C2A17906!2383