MSDN: Building Web Apps on the MEAN Stack with OData in Microsoft Azure

Apologize for the delay, this article was actually published last month. If your a .NET developer specifically with ASP.NET (MVC, Web API/REST, Entity Framework, OData, etc.) and have heard or had interest in the MEAN stack, especially Node.JS, server side development in JavaScript – made possible with Google’s V8 open source JavaScript engine, have a quick read (and YES the entire MEAN stack will run in Azure just as your traditional .NET web stack would).

MSDN: Building Web Apps on the MEAN Stack with OData in Microsoft Azure

Online: http://msdn.microsoft.com/en-us/magazine/dn857363.aspx
Download: https://lelong37.files.wordpress.com/2015/01/mdn_1412dg.pdf
Source code: http://msdnmeanstack.codeplex.com
Live Demo: http://meanjaydatakendo.azurewebsites.net

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!

TwilioCON 2012 Developer & Doers @ San Francisco, CA – Day 2

TwilioCON 2012 Hackathon was fun, teamed up with John Palmer @citriusjohn and kicked around a few ideas however figured it would be best if we waited for TwilioCON 2013 to actually compete.

It was great to finally meet some of the people I’ve done some work with remotely and/or through online meetings e.g. Evan Cummack (@cummack), Devin Rader (@devinrader), among other people at TwilioCON. Another highlight of the second day was hearing the good news that we are officially been approved to as an Authorized Twilio SI (Systems Integrator), thanks Greg, Cheetan, and the rest of Twilio’s SI division for making this happen. Wrapping up I had a blast, and hopefully we can do it again next year…!

TwilioCON 2012 Developer & Doers @ San Francisco, CA – Day 1

Had an awesome day yesterday @ TwilioCON 2012 here at San Francisco, CA. Attended some pretty interesting key notes and lab sessions. I really appreciate some of the new features added especially working with call queues (http://www.twilio.com/voice/features/queue) with Twilio Client (http://nuget.org/packages/Twilio.Client).

The demo of full browser soft phone was totally awesome, and having almost full Twilio Client ability on the client side as you would on the server side was also rocked! As day one of TwilioCon 2012 was wrapping up and I started packing, guess what?! Saw Scott Guthrie and fellows walk through the door, will keep you guys posted on what this is about in tomorrows blog, or you can keep an eye on my Twitter (http://twitter.com/lelong37) for updates on this.

Last but not least thanks for the Twilio VIP Dinner invite, had a blast!

Migrating & Deploying Telerik’s Sitefinity CMS to Windows Azure

So I started to migrate Telerik’s Sitefinity CMS to Windows Azure this weekend and based on all the documentation out there…, well let’s just say it wasn’t as comprehensive as I thought it should be; hence this blog post for others that may need some supplementary guidance with http://www.sitefinity.com/documentation/documentationarticles/installation-and-administration-guide/deployment/deploying-sitefinity-projects-to-windows-azure/.

I’ll keep this in Azure for as long as I can, mean while here’s the Url of the sample Sitefinitity instance that was deployed to Azure in this blog post: http://sitefinityinazure.cloudapp.net.

Note: Throughout this process you can debug, build, however do not perform a clean or rebuild on the solution. The Sitefinity assemblies are included and referenced from the bin folder, performing a clean or rebuild will wipe these assemblies out unless you copy them out of the bin folder into another folder (e.g. folder named “Assemblies”) and re-reference them. This is a known issue when doing a clean or rebuild http://www.sitefinity.com/devnet/kb/sitefinity-4-x/an-error-%E2%80%9Ccould-not-load-file-or-assembly-telerik-sitefinity-migrationcontracts-or-one-of-its-dependencies-%E2%80%9D-is-thrown-when-you-rebuild-or-clean-your-sitefinity-4-2-project.aspx. If time permits I will post another blog for this solution.

For the purpose of what actually works, we will stick with the sequence of these steps:

  1. Start with a fresh install and instance of a Sitefinity site
  2. Create a test page, with a content widget
  3. Create the SQL Azure Instance
  4. Create the SQL Azure Blob Storage
  5. Migrate the local database to SQL Azure and assert that the application runs fine locally using the SQL Azure instance in the cloud
  6. Configure the Azure Blob storage
  7. Add and configure the Azure Web Role to the solution and confirm that it works locally with the Azure emulators, this installs with Azure SDK (optional step)
  8. Deploy our Sitefinity instance into Windows Azure as a Cloud Service, we will provision the Cloud Service as part of the first time deployment to Azure

So let’s start with our Sitefinity instance, for the purpose of this post I’ve created a new Sitefinity instance (with the Community license) and named it SitefinityInAzure running on a local SQL2008R2 database instance. Database name will also be named the same as the Sitefinity site name, SitefinityInAzure. I would highly recommend you practice this migration on a fresh new blank site rather than on actual production site until you get the hang of things.

Start off by making sure your local instance of Sitefinity is golden, we can do this by spinning up Sitefinity Project Manager and browse to Admin area by clicking [Go To Administration] on the context menu.

Do a quick sanity check and validate that everything on the Administration is golden.

Go ahead and create a test page. This can be done by going to Pages > Create New Page > Save > Publish.

View the page.

Now let’s start the database migration first, first in order is to create and setup an instance of SQL Azure.

Go ahead and copy the connection string so that we can use this in a bit.

Launch your Sitefinity instance with Visual Studio.

Open up the DataConfig.config file, where Sitefinity’s connection string is stored and let’s go ahead and modify it to use SQL Azure.

Before:


<?xml version="1.0" encoding="utf-8"?>
<dataConfig xmlns:config="urn:telerik:sitefinity:configuration" xmlns:type="urn:telerik:sitefinity:configuration:type" config:version="5.1.3270.0" initialized="True">
	<connectionStrings>
		<add connectionString="data source=localhost;Integrated Security=SSPI;initial catalog=SitefinityInAzure" providerName="System.Data.SqlClient" dbType="MsSql" name="Sitefinity" />
	</connectionStrings>
	<urlEvaluators>
		<add description="ForumsEvaluatorDescription" resourceClassId="ForumsResources" type="Telerik.Sitefinity.Forums.Web.ForumsEvaluator" enabled="True" name="Forums" />
	</urlEvaluators>
</dataConfig>

After (note the dbType attribute has been set to SqlAzure):


<?xml version="1.0" encoding="utf-8"?>
<dataConfig xmlns:config="urn:telerik:sitefinity:configuration" xmlns:type="urn:telerik:sitefinity:configuration:type" config:version="5.1.3270.0" initialized="True">
	<connectionStrings>
		<add connectionString="data source=[your-azure-servername-goes-here].database.windows.net;initial catalog=SitefinityInAzure; User ID=[your-azure-username-for-sqlazure]; Password=[your-password-for-sqlazure-goes-here]" providerName="System.Data.SqlClient" dbType="SqlAzure" name="Sitefinity" />
	</connectionStrings>
	<urlEvaluators>
		<add description="ForumsEvaluatorDescription" resourceClassId="ForumsResources" type="Telerik.Sitefinity.Forums.Web.ForumsEvaluator" enabled="True" name="Forums" />
	</urlEvaluators>
</dataConfig>

Update your web.config for Azure configuration, first uncomment the sectionGroup with name Telerik.

Before:


  <configSections>
    <section requirePermission="false" name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <!--<sectionGroup name="telerik">
      <section name="sitefinity" type="Telerik.Sitefinity.Configuration.SectionHandler, Telerik.Sitefinity" requirePermission="false" />
    </sectionGroup>-->
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>

After:


  <configSections>
    <section requirePermission="false" name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <sectionGroup name="telerik">
      <section name="sitefinity" type="Telerik.Sitefinity.Configuration.SectionHandler, Telerik.Sitefinity" requirePermission="false" />
    </sectionGroup>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>


Uncomment the Telerik section (towards the end of the web.config file).

Before:


  <!-- Begin telerik section  -->
  <!--<telerik>
    <sitefinity>
      <environment platform="WindowsAzure"/>
      <sitefinityConfig storageMode="Database" />
    </sitefinity>
  </telerik>-->
  <!-- End telerik section  -->


After:


  <!-- Begin telerik section  -->
  <telerik>
    <sitefinity>
      <environment platform="WindowsAzure"/>
      <sitefinityConfig storageMode="Database" />
    </sitefinity>
  </telerik>
  <!-- End telerik section  -->
</configuration>

Now that our Sitefinity app is ready to work with SQL Azure, let’s migrate our actual data first. For this you will need to download SQL Azure Migration Wizard (http://sqlazuremw.codeplex.com/).

Launch an instance of SQL Azure Migration Wizard and select: Analyze / Migrate – SQL Database option and click next.

Select the current SitefinityInAzure local database that you be migrating from (source).

Generate the SQL Script (that SQL Azure Migration Wizard will use to execute on the target SQL Azure Instance, that we created earlier).

Great now let’s run our local instance of SitefinityInAzure and verify that our on-premise app is working with our off-premise SQL Azure instance where we just migrated our data too. We can do this by browsing to the Administration page.

Great, we are half way there now. All we have left is getting our application Azure ready and deployed to Azure.

Let’s start off by making sure that all the required assemblies will be deployed in the Azure by expanding the references folder and checking that all the required assemblies to be deployed to Azure by setting the “Copy Local” to true.

Be default all of the required assemblies should already have the property “Copy Local” set to true set already, so there should not be any work here required besides a quick check, this step can be skipped.

This should be done for all references with the exception of:

  • System.Windows
  • Microsoft.WindowsAzure.ServiceRuntime
  • MySql.Data
  • Telerik.OpenAccess.MySql.Data
  • Interop.DexterLib

Now let’s go ahead and add our Azure Web Role to our solution, right click the Solution and add a Windows Azure Cloud Service project from VS templates and give it a name SitefinityWebRole.

When prompted for what type of role you would like to include, DO NOT SELECT ANY OPTION (the right side selected list box should be blank), we will manually add this in the next step, click OK.

Now with the context menu of the SitefinityWebRole Roles folder click “Web Role Project in solution” and choose SitefinityWebApp as the project to be added as the WebRole.

Let’s go ahead and create a blob storage in Azure so that Sitefinity will have a place to log to, go ahead and name the Blog Storage: sitefinityinazure.

Copy the primary access key for you Azure Blob Storage.

Let’s go ahead and configure the new SitefinityWebRole by go the properties of the newly added Role (click on the properties of the context menu of SitefinityWebRole/Roles/SitefinityWebApp).

Configuration, make sure the Service Configuration “Cloud” is selected.

Endpoints

Local Storage

Registry, the last thing we need to do is reorder some permissions, or you may get the an exception with the Azure runtime starts up in debug mode. Browse to HKLM/Software/Microsoft/Windows Azure with Regedt32, you will get a prompt that this action will Re-Order your permissions click OK, and on the next screen click Apply then OK. Do the same for the HKLM/Software/Microsoft/Windows Azure Emulator.

The WebRole configuration is now complete, now before deploying anything, let’s validate that everything works fine locally with the Azure SDK and the Azure Emulators by hitting F5 or clicking the debug button within Visual Studio.

If you get this prompt to modify your web.config for debugging, go ahead and click OK.

You should see the Windows Azure Debugging Environment spin up (this could take a minute or two).

We should see the “About Us” test page we created earlier running on our locally on our Windows Emulators.

Now, let’s deploy to Azure!

You can skip this step, however I went ahead and configured this so that I could RDP into this instance.

Click on Publish, this could take a while since you are also provisioning your WebRole service for the first time as well as deploying. Once completed you should see this area in your Windows Azure Activity Log.

Click on the Website Url link.

I will leave the Windows Azure resources/instances that were created for the purposes of this blog for as long as I can, until then you can view the actual Sitefinity instance that was used to deploy in Azure at this Url: http://sitefinityinazure.cloudapp.net.

Made a few changes to the content on the “About Us” page we created earlier just to excercise some of Sitefinity’s CMS abilities 😉

Log-in to the back end (don’t want to state the obvious here, however our Url is http://sitefinityinazure.cloudapp.net/Sitefinity/Dashboard, in Azure).

Sitefinity has been migrated and deployed into Windows Azure!

One last note that I thought was interesting, was how easy it is connecting with RDP to your Azure Cloud Service instance.

Log in to the RDP session with your server credentials you used when deploying.

Happy CMS’ing..! 🙂

Attending Microsoft Cloud Summit: Windows Azure and Beyond‬ in Dallas, TX!

Attending Microsoft Cloud Summit: Windows Azure and Beyond‬ in Dallas, TX.
https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032515368&Culture=en-US

New Azure Portal UI rocks…!

Thought this was an interest converation during the developer session dinner between my friend Noah a C# MVP and a VB.NET MVP while Tom and I tune in. Since we’re graced with the opportunity to be at the dinner table why not record it!