Tuesday, July 10, 2012

Deploying ASP.NET MVC4 Single Page Applications on Windows Azure Websites

An anonymous poster on my fifth post about Single Page Applications (SPA) asked if it was possible to deploy SPAs on Windows Azure. As it turns out the Windows Azure Web Site offering makes this very easy to do if you build your SPA as outlined in that post.

Windows Azure Websites

Windows Azure Web Sites have been recently announced by Microsoft and are at the time of writing released as a preview feature on Windows Azure. 

If you don't have a Windows Azure account already you can sign up for the free 90-day trial. Since the feature is still in preview, you can sign up for free as well on your Windows Azure Account Page. Activation can take a full day.


Windows Azure Account - Preview Features


Once you have received the e-mail message that your account has been activated, you can navigate to the brand new Windows Azure Portal and create a new website. Since I will be deploying my 'DeliveryTracker' Single Page Application, I will also need to create a database.

New - Web Site - Create With Database

Follow the wizard that appears. If you don't have a SQL Database in Azure already allow the wizard to create one for you.

SQL Databases in Windows Azure

SQL databases are part of the Data Management offering of Windows Azure. You can either manage these databases from the portal or via the SQL Server Management Studio (SSMS). Because I already had an existing SQL database in Azure, I chose to migrate my data from my local SQL Server using SSMS.

Deploy Database to SQL Azure
If your firewall allows it (port 1433 needs to be open) you can deploy to SQL Azure directly. If not, you can choose to 'Export the Data-tier application'. This generates a .bacpac file that you can import in the management window of your SQL Azure.

In any case you need to make sure that the firewall of the SQL Azure server accepts the ip address of your local machine to connect. This you can also manage from the SQL Azure portal

Deploying DeliveryTracker

Now that everything is in place, the actual deployment is quite simple:
  • Download the DeliveryTracker application, unzip it and open it with Visual Studio 2010.
  • Open the web.config file and rename the 'DefaultConnection' to 'AppDbContext' which is the name of the actual Entity Framework Code First DbContext that is used.
  • Right-click the DeliveryTracker project and choose 'Publish...'. A wizard opens.
  • In the first screen ('Profile') of the wizard you can import a publishing profile. This publishing profile is available for download in the management console for your new website in the Azure portal
  • In the third screen ('Settings') of the wizard, make sure that the connection string is called 'AppDbContext' and is pointing to the correct database that was created above in SQL Azure.
  • Click Publish
Visual Studio will now start deploying the website to Windows Azure. It doesn't take too long to finish. You can visit the DeliveryTracker site that I deployed while writing this post

I probably should add a 'Reset Test Data' button somewhere :)

Friday, June 29, 2012

Building Single Page Apps with ASP.NET MVC4 - Part 5 - using ASP.NET MVC4 RC

This blogpost is part of a series about building Single Page Applications using ASP.NET MVC 4. Parts 1 through 4 use ASP.NET MVC4 beta but this post will focus on building SPA after upgrading to ASP.NET MVC 4 RC

  1. Single Page Applications - Part 1 - Basic Desktop application
  2. Single Page Applications - Part 2 - Advanced Desktop Application
  3. Single Page Applications - Part 3 - Basic Mobile Application
  4. Single Page Applications - Part 4 - Sorting, Filtering and Manipulation data with upshot.js
  5. Single Page Applications - Part 5 - Using ASP.NET MVC4 RC

In the release candidate for ASP.NET MVC 4, Microsoft decided to remove the Single Page Application template and supporting files. This was no surprise as I already concluded in part 4 of this series that SPA was far from being ready.

So how do you build an SPA after installing MVC4 RC? Peter Porfy described one solution on his blog. Essentially he is taking the latest nightly build of ASP.MVC4 and correcting the errors. The downside to his solution is that it looks like a lot of work and this is still very unstable.

The key to my solution lies in the fact that SPA written against ASP.NET MVC4 beta, still run on machines with only the MVC4 RC installed. All the SPA essentials can be found in NuGet packages. As it happens, I had created a new MyGet feed to enable NuGet Package Restore so I didn't have to include the SPA packages every time I released new code on my blog.

I realize that this means you are not really using the MVC4 RC but at least you can continue building SPA after installing it until Microsoft releases a new and supported version.

Starting a new SPA using VS 2010 with ASP.NET MVC 4 RC

Follow these steps to get started:
ASP.NET SPA MVC4 Beta feed

Create a new MVC project and install the SPA NuGet packages from the PM Console:

  • Menu --> File --> New --> Project --> ASP.NET MVC4 Web Application --> Internet Application
  • Find the packages.config file and remove the line that includes the "EntityFramework". This version 5.0.0-rc RC is incompatible with the version 4.3.1 that we need. The line looks like this:
<package id="EntityFramework" version="5.0.0-rc" targetFramework="net40"/>
  • Go to your project references and remove the reference to EntityFramework here as well. Below screenshot shows both action:
  • Menu --> Tools --> Library Package Manager --> Package Manager Console

Make sure that 'Package source:' points to my MyGet feed otherwise you will download the packages from the official not-supported NuGet source

Install-Package SinglePageApplication.CSharp
Install-Package EntityFramework

After closing and re-opening the solution you're now ready start developing a new SPA

Migrating DeliveryTracker to MVC4 RC

I took the source code of DeliveryTracker4 from my SkyDrive and used this as a starting point for a new project that I also conveniently had called 'DeliveryTracker' to minimize namespace problems

  • This is the list of files and folders I copied and included into the new 'DeliveryTracker' project.
    .\DeliveryTracker\Content\Site.mobile.css
    
    .\DeliveryTracker\Controllers\DataServiceController.cs
    .\DeliveryTracker\Controllers\ViewSwitcherController.cs
    
    .\DeliveryTracker\Models\AppDbContext.cs
    .\DeliveryTracker\Models\DomainModel.cs
    
    .\DeliveryTracker\Scripts\App
    
    .\DeliveryTracker\Views\Home
    .\DeliveryTracker\Views\Shared\_Layout.cshtml
    .\DeliveryTracker\Views\Shared\_Layout.mobile.cshtml
    .\DeliveryTracker\Views\Shared\_SpaScripts.cshtml
    .\DeliveryTracker\Views\Shared\_ViewSwitcher.cshtml
    
  • I changed the connectionstring in both the connectionStrings and entityFramework sections of the web.config file
    <connectionStrings>
      <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=.;Initial Catalog=DeliveryTracker;Integrated Security=SSPI" />
    </connectionStrings>
    
    <entityFramework>
      <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
        <parameters>
          <parameter value="Data Source=.; Integrated Security=True; MultipleActiveResultSets=True" />
        </parameters>
      </defaultConnectionFactory>
    </entityFramework>
    

There were some additional steps to take before I could get my DeliveryTracker sample to function correctly.

  • Update the global.asax.cs file to add my database initializer to the Application_Start function
    #if DEBUG
    System.Data.Entity.Database.SetInitializer
      (new DeliveryTracker.Models.AppDbContextSeedInitializer());
    #endif
    
  • Update the site.css file to include the two classes that were added to highlighted changes on screen
    .delivered
    {
        text-decoration: line-through;
        color: #008000;
    }
    
    .updated
    {
        background-color: #FFFF00;
    }
    
  • Update the RouteConfig.cs file to make the DataService controller reachable to accept POST'ed updates.
    routes.MapHttpRoute(
      "DataService", // Route name
      "api/DataService/{action}", // URL with parameters
      new { controller = "DataService" } // Parameter defaults
    );
    

Download the source code

You can download the source code from my SkyDrive (Visual Studio 2010 project)

Tuesday, April 3, 2012

Building Single Page Apps with ASP.NET MVC4 - Part 4 - Sorting, Filtering and Manipulating data with upshot.js

This blogpost is part of a series about building Single Page Applications using ASP.NET MVC 4 beta. When writing this post I assumed you have read the previous ones. The code snippets are just the incremental changes that are needed since the third. post I'd advise to just download the completed source code directly

  1. Single Page Applications - Part 1 - Basic Desktop application
  2. Single Page Applications - Part 2 - Advanced Desktop Application
  3. Single Page Applications - Part 3 - Basic Mobile Application
  4. Single Page Applications - Part 4 - Sorting, Filtering and Manipulation data with upshot.js
  5. Single Page Applications - Part 5 - With MVC4 RC

This post was more or less triggered by this comment on my first post in this series. James wanted to avoid creating GET functions for each single case. I already replied that he could take advantage of the OData features of WebApi but I think a dedicated post is needed since this is a topic where upshot is showing its limitations.

Updating to the latest packages

First thing I did -starting from the code from my previous post- was to upgrade all packages to the latest version. I had to rename the existing upshot.knockout.extensions.js file before updating, since I had modified it and the package manager would skip it instead of updating it. Since I'm working with C#, I used this command in the package manager console
Install-Package SinglePageApplication.CSharp

This will upgrade some dependencies and add new files such as T4 templates to the project. You will need to update some of the links to javascript files because for example knockout-2.0.0.js is upgraded and renamed to simply knockout.js. I also created a namespace 'deliveryTracker' and put my DeliveriesViewModel in it.

After the package update the UpshotContext HtmlHelper function is now available. So in my desktop and mobile views I can now replace the Javascript call to upshot.metadata with following code:

@(Html.UpshotContext(bufferChanges: true)
          .DataSource<DeliveryTracker.Controllers.DataServiceController>
                  (x => x.GetDeliveriesForToday())
          .ClientMapping<DeliveryTracker.Models.Customer>
                  ("deliveryTracker.Customer")
          .ClientMapping<DeliveryTracker.Models.Delivery>
                  ("deliveryTracker.Delivery")
)

<script type="text/javascript">
    $(function () {
        var model = new deliveryTracker.DeliveriesViewModel();
        ko.applyBindings(model);
    });
</script>

This will generate javascript that calls upshot.metadata and also sets up a remote datasource called 'DeliveriesForToday. This means I can now remove the code that creates a new RemoteDataSource in the constructor of my DeliveryViewModel and get it from upshot.dataSources.DeliveriesForToday

/// <reference path="_references.js" />
(function (window, undefined) {
    var deliveryTracker = window["deliveryTracker"] = {};

    deliveryTracker.DeliveriesViewModel = function () {

        // ... snip

        self.dataSource = upshot.dataSources.DeliveriesForToday;

        // ... snip
    };

    deliveryTracker.MobileDeliveriesViewModel = function () {
        //inherit from DeliveriesViewModel
        var self = this;
        deliveryTracker.DeliveriesViewModel.call(self);

        // ... snip
    };

    // ... snip ...

    window["deliveryTracker"] = deliveryTracker;
})(window);

Note the calls to the ClientMapping functions which are used to indicate that I want to map the incoming data to the Customer and Delivery objects that I have created in javascript

Sorting and Filtering when fetching data

        // Initialize remote and local datasource
        self.dataSource = upshot.dataSources.DeliveriesForToday;
        self.dataSource.setFilter({ property: "IsDelivered", 
                                    operator: "==", value: false } ); //,
        self.dataSource.setSort({ property: "Customer/Name", descending: false });
        self.dataSource.refresh();

The remoteDatasource is built on top of an OData service. The above request will generate this HTTP GET

 
GET http://localhost:61907/api/DataService/GetDeliveriesForToday?%24filter=IsDelivered+eq+false&%24orderby=Customer%2FName HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: application/json, text/javascript, */*; q=0.01
Referer: http://localhost:61907/
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: localhost:61907
Connection: Keep-Alive

Note how you can sort and filter on a property of an associated entity by using a forward slash (...property: "Customer/Name"...) because this is supported by OData. The data also got filtered so the browser only received those deliveries where the IsDelivered property was set to false. Unfortunately upshot does not expose the full power of OData to the client application.

Sorting and Filtering the local data

Once data is loaded into the browser it can still be manipulated. In order to see the effect of a manipulation while data is being edited, I decided to set the 'allowRefreshWithEdits: true' property on the local datasource. This lets me sort or group the data when edits are still pending (not uploaded back to the server).
self.localDataSource = upshot.LocalDataSource({ source: self.dataSource, 
                        autoRefresh: true, allowRefreshWithEdits: true });

self.deliveries = self.localDataSource.getEntities();
self.deliveriesForCustomer = self.deliveries.groupBy("Customer");

In order to demonstrate sorting and grouping on the local data, I thought it would be fun to include the geolocation capabilities of the current browsers. If a user allows the application to read his current location, the application will sort the data by 'distance from user'.

I added latitude and longitude data to the my server-side Customer domain entity. This entity and the Delivery entity are both mapped to their javascript counterparts, like so:

deliveryTracker.Customer = function (data) {
 var self = this;

 self.CustomerId = ko.observable(data.CustomerId);
 self.Name = ko.observable(data.Name);
 self.Address = ko.observable(data.Address);
 self.Latitude = ko.observable(data.Latitude);
 self.Longitude = ko.observable(data.Longitude);

 //only exists client-side
 self.DistanceFromMe = ko.observable('unknown');

 upshot.addEntityProperties(self, "Customer:#DeliveryTracker.Models");
};

deliveryTracker.Delivery = function (data) {
 var self = this;

 self.DeliveryId = ko.observable(data.DeliveryId);
 self.CustomerId = ko.observable(data.CustomerId);
 self.Customer = ko.observable(data.Customer ? 
             new deliveryTracker.Customer(data.Customer) : null);
 self.Description = ko.observable(data.Description);
 self.IsDelivered = ko.observable(data.IsDelivered);

 upshot.addEntityProperties(self, "Delivery:#DeliveryTracker.Models");
};

It's important to note that upshot will map the Delivery object automatically but not the Customer object even when specifying it in the ClientMapping function on the UpshotContext. So whenever upshot tries to map the Delivery object, I create an observable Customer object myself. See the highlighted lines above

Now add this to the constructor of the DeliveriesViewModel:

var self = this;
var self.position
// Initialize geolocation
if (navigator.geolocation) {
 self.deliveries.subscribe(CalculateDistanceFromMe); //data is refreshed
 self.position.subscribe(CalculateDistanceFromMe);   //position is refreshed

 navigator.geolocation.getCurrentPosition(function (position) {
  self.position(position);
 });
}

// Recalculate distance from deliverer when data or geolocation changes
function CalculateDistanceFromMe() {

 if (self.position() && self.deliveriesForCustomer 
            && self.deliveriesForCustomer().length > 0) {

  //calculate and update distances
  ko.utils.arrayForEach(self.deliveriesForCustomer(), 
                                           function (keyValuePair) {
   var customer = keyValuePair.key;
   var dist = geolocationUtils.calculateDistance(
                                           self.position().coords.latitude, 
                                           self.position().coords.longitude, 
                                           customer.Latitude(), 
                                           customer.Longitude());
   customer.DistanceFromMe(dist.toFixed(2));
  });

  //sort local datasource on DistanceFromMe ascending
  self.localDataSource.setSort(function (delivery1, delivery2) {
   var value1 = delivery1.Customer().DistanceFromMe();
   var value2 = delivery2.Customer().DistanceFromMe();
   return (value1 - value2);
  });
                self.localDataSource.refresh(); //trigger refresh with sorting

  //sort data grouped by customer
  self.deliveriesForCustomer().sort(function (left, right) {
   var value1 = left.key.DistanceFromMe();
   var value2 = right.key.DistanceFromMe();
   return (value1 - value2);
  });
 };
};

So what's happening:

  • self.position is a knockout (ko) observable that holds the current location.
  • CalculateDistanceFromMe is triggered when self.position or self.deliveries gets updated
  • calculateDistance is a function I found on HTML5 Rocks and wrapped in the 'geolocationUtils' namespace. It is called for each customer when the data gets refreshed
  • The calculated distance is stored in the client-side observable customer.DistanceFromMe property
  • Sorting on the localDataSource passes a sorting function to the setSort method on the dataSource.
  • Sorting on the deliveriesForCustomer passes a sorting function to the sort method off the knockout observable array (wrapper around javascript arrays)

Conclusions

Sorting is a tricky subject when mixing technologies like Microsoft is doing in this beta release of Single Page Applications. Different objects allow different features depending on which part of the abstraction leaks through

The remoteDataSource object is leaking OData conventions so when sorting or filtering you need to comply with the OData Orderby and Filter uri-conventions. For example you can easily sort on the Customer/Name property of an entityset of Delivery objects.

The localDataSource object however has its own implementation of sorting, filtering and grouping. Besides passing a sorting function, it also allows passing in a property name. So you can sort on the CustomerId of an entityset (of Delivery objects) but you cannot sort on Customer/Name

The GetEntities method on a localDataSource returns a Knockout observableArray. This object exposes a wrapper around the built-in javascript arrays and exposes the same functionality which again is different from the remote or local datasources.

What annoys me is that knockout and javascript both have a syntax for navigating associated entities, being the dot operator. If a remoteDataSource allows to sort by "Customer/Name", at least the localDataSource should expose the same interface. Also when binding data into the UI, you can easily do the following:


So navigating the associated entities using the dot operator works fine when data-binding but not when sorting, filtering, grouping, ...

As the Single Page Application are still in an early phase I suppose this is to be expected. In my opinion it's still way too soon to use upshot in its current form in any serious development.

Download the source code

You can download the code (Visual Studio 2010 project) from my Skydrive