113 Comments

image

Much has been said about the Node.js’s great performance so I wanted to test out how it compares to an ASP.NET Web Api backend.I created a simple server for both of the platforms which accepts a POST-request and then responds back with the request’s body.

Update 24.6.2012: Updated tests with some tweaks.

The Node.js and ASP.NET Web Api implementations

Here’s the Node.js code:

var express = require('express')
    , app = express.createServer();

app.use(express.bodyParser());

app.post('/', function(req, res){
    res.send(req.body);
});

app.listen(3000);

And here’s the ASP.NET Web Api controller:

    public class ValuesController : ApiController
    {
        // POST /api/values
        public Task<string> Post()
        {
            return this.ControllerContext.Request.Content.ReadAsStringAsync();
        }
    }

Benchmark

I used Apache’s ab tool to test the performance of the platforms. The benchmark was run with the following settings:

  • Total number of requests: 100 000
  • Concurrency: 100

The benchmark (test.dat) contained a simple JSON, taken from Wikipedia.

{
     "firstName": "John",
     "lastName" : "Smith",
     "age"      : 25,
     "address"  :
     {
         "streetAddress": "21 2nd Street",
         "city"         : "New York",
         "state"        : "NY",
         "postalCode"   : "10021"
     },
     "phoneNumber":
     [
         {
           "type"  : "home",
           "number": "212 555-1234"
         },
         {
           "type"  : "fax",
           "number": "646 555-4567"
         }
     ]
 }

Here’s the whole command which was used to run the performance test:

ab -n 100000 -c 100 -p .test.dat -T 'application/json; charset=utf-8' http://localhost/

The performance test was run 3 times and the best result for each platform was selected. The performance difference between the test runs was minimal.

Test Environment

The benchmark was run on a Windows Server 2008 R2, hosted on an c1.medium Amazon EC2 –instance:

image

Specs of the instance

  • 1.7GB memory
  • 5 EC2 Compute Units (2 virtual cores)

Versions

  • Node.js: 0.6.17
  • ASP.NET Web Api: The official beta release.
  • IIS: 7

Both the Node and IIS –servers were run with their out-of-the-box settings.

Benchmark Results

image

 Web ApiNode.js
Time taken (in s)89.9541.65
Requests per second1111.692400.89
Time per request (in ms)89.9541.65
Failed requests00

Conclusion

The out-of-the-box performance of the Node.js seems to be better than the performance of the ASP.NET Web Api + IIS7. Tweaking the IIS7’s settings could make the ASP.NET Web Api perform better but for this test the default settings of IIS7 were used.

imageTcpClient is a class in .NET Framework which “provides simple methods for connecting, sending, and receiving stream data over a network”. TcpClient hides the details of working with sockets and it’s a simple way to open connections and work with TCP. Unfortunately, TcpClient is not available in Windows Phone version of .NET Framework.

SocketEx.TcpClient – Unofficial, only little tested TcpClient for Windows Phone

SocketEx.TcpClient is a MIT-licensed TcpClient for Windows Phone which aims to make working with Windows Phone sockets easy. Compared to the TcpClient in full .NET Framework, SocketEx.TcpClient isn’t 100% compatible and some of the features aren’t implemented at all.

Please note that the library works in a synchronous blocking mode. This means that if you use the TcpClient directly from the UI-thread, you will block the UI from updating.

The library hasn’t gone through an exhaustive testing so there may be issues. The code is based on the “Crystalbyte Networking for Silverlight” project, available from the CodePlex. Almost all of the code is from that neat library, but I adjusted it a little to get it working with Windows Phone and fixed out some threading issues.

The usage

With SocketEx.TcpClient you don’t work with the low-level Socket and SocketAsyncEventArgs-classes. Instead you create a new TcpClient and then operate its stream using either a StreamReader or a StreamWriter. This is easier than it sounds.

I’ve previously written about how to operate with the built-in Socket and SocketAsyncEventArgs classes.

Now, let’s do those same examples with SocketEx.TcpClient.

SocketEx.TcpClient – How To Open a Connection

We can open the connection by passing the server address and server port as parameters to TcpClient.

            var serverAddress = "www.google.fi";
            var serverPort = 80;

            var connection = new TcpClient(serverAddress, serverPort);

SocketEx.TcpClient – How To Receive a Message

To read a message we need a StreamReader.

            var connection = CreateConnection();
            var stream = connection.GetStream();

            var reader = new StreamReader(stream);

            string message;
            while ((message = reader.ReadLine()) != null)
            {
                Debug.WriteLine(message);
            }

SocketEx.TcpClient – How To Send a Message

To write a message we need a StreamWriter.

            var connection = CreateConnection();
            var stream = connection.GetStream();

            using (var writer = new StreamWriter(stream))
            {
                var request = "GET / HTTP/1.1rnHost: " + serverAddress + "rnConnection: Closernrn";

                writer.WriteLine(request);
            }

Project’s home and sample app

The SocketEx.TcpClient lives in GitHub. The repository contains the TcpClient and a sample app which uses it.

Nuget

Easiest way to get started with SocketEx is to use NuGet. The package name is SocketEx.

Binary

The binary version of SocketEx is available for download from GitHub.

12 Comments
  •   Posted in: 
  • UWP

imageIn this WinRT tutorial we will build a single page application using the following building blocks:

  • XAML & C#
  • MVVM
  • Data binding
  • GridView
  • SemanticZoom –control

We will start from the scratch, using the “Blank application” template. Then we continue by creating the view model. After the view model is ready we will create the view by adding a page using the “Grouped Items Page” template. At last step we’re going to modify the page to support the SemanticZoom –control.

Update: Make sure to read about the GridView's performance problems.

Update: The examples and source code has been updated to work with Windows 8 RTM.

1. Creating the project

Let’s start by creating a new application using the “Blank application” template. This template contains a single page, “BlankPage.xaml”, which we can delete. Our application will contain only one page and we’re going to add this as a last step, using the built-in “Grouped Items Page” template. This template contains the GridView which we’ll use to to implement the semantic zoom.

image

But before creating the view, we’ll need a view model.

2. Creating the view model

Add a C# class and name it “MoviesPageViewModel.cs”. This is going to be our view model and it’ll work as the DataContext for our view.

image

The models

Our application shows movies grouped by their categories. For this we’ll need a new class which represents a single movie. This class can be added to the MoviesPageViewModel:

public class Movie

    {

        public string Title { get; set; }

        public string Subtitle { get; set; }

        public string Image { get; set; }

        public string Category { get; set; }

    }

We also need a model which represents a single movie category:

public class MovieCategory
    {
        public string Title { get; set; }
        public List<Movie> Items { get; set; }
    }

The data

We’re not going to connect our application into the net, instead we’re using static data. The movie instances can be created inside the MoviePageViewModel’s constructor:

var movies = new List<Movie>
                             {
                                 new Movie {Title = "The Ewok Adventure", Category = "Adventure", Subtitle = "The Towani family civilian shuttlecraft crashes on the forest moon of Endor.", Image = "http://cf2.imgobject.com/t/p/w500/y6HdTlqgcZ6EdsKR1uP03WgBe0C.jpg"},
                                 new Movie {Title = "The In-Laws", Category = "Adventure", Subtitle = "In preparation for his daughter's wedding, dentist Sheldon ", Image = "http://cf2.imgobject.com/t/p/w500/9FlFW9zhuoOpS8frAFR9cCnJ6Sg.jpg"},
                                 new Movie {Title = "The Man Called Flintstone", Category = "Adventure", Subtitle = "In this feature-length film based on the Flintstones TV show", Image = "http://cf2.imgobject.com/t/p/w500/6qyVUkbDBuBOUVVplIDGaQf6jZL.jpg"},

                                 new Movie {Title = "Super Fuzz", Category = "Comedy", Subtitle = "Dave Speed is no ordinary Miami cop--he is an irradiated Miami cop ", Image = "http://cf2.imgobject.com/t/p/w500/bueVXkpCDPX0TlsWd3Uk7QKO3kD.jpg"},
                                 new Movie {Title = "The Knock Out Cop", Category = "Comedy", Subtitle = "This cop doesn't carry a gun - his fist is loaded!", Image = "http://cf2.imgobject.com/t/p/w500/mzlw8rHGUSDobS1MJgz8jXXPM06.jpg"},
                                 new Movie {Title = "Best Worst Movie", Category = "Comedy", Subtitle = "A look at the making of the film Troll 2 (1990) ", Image = "http://cf2.imgobject.com/t/p/w500/5LjbAjkPBUOD9N2QFPSuTyhomx4.jpg"},

                                 new Movie {Title = "The Last Unicorn", Category = "Fantasy", Subtitle = "A brave unicorn and a magician fight an evil king", Image = "http://cf2.imgobject.com/t/p/w500/iO6P5vV1TMwSuisZDtNBDNpOxwR.jpg"},
                                 new Movie {Title = "Blithe Spirit", Category = "Fantasy", Subtitle = "An English mystery novelist invites a medium", Image = "http://cf2.imgobject.com/t/p/w500/gwu4c10lpgHUrMqr9CBNq2FYTpN.jpg"},
                                 new Movie {Title = "Here Comes Mr. Jordan", Category = "Fantasy", Subtitle = "Boxer Joe Pendleton, flying to his next fight", Image = "http://cf2.imgobject.com/t/p/w500/9cnWl7inQVX6wznjYNmQmJXVD6J.jpg"},
                             };

Grouping the data

Then we can group the movies by their categories and create a “MovieCategory” instance for each category:

var moviesByCategories = movies.GroupBy(x => x.Category)
                .Select(x => new MovieCategory { Title = x.Key, Items = x.ToList() });

Publishing the data from the view model to the view

We’re going to use data binding to create the view. As we now have the movies in the format we want, we’ll only have to create a public property which the view can bind against. For this we’ll add a new property to the view model:

public List<MovieCategory> Items { get; set; }

The property can be initialized after we have grouped the movies:

Items = moviesByCategories.ToList();

And that’s it. We now have the view model ready and it’s quite simple. The data is created inside the constructor and the view model only has one public property. The view can bind its GridView against this property in order to access the movies.

It’s important to notice that the view model’s public property contains grouped data. We want to display the data in a grouped format, where each movie category is displayed separately. This is why the property contains a list of data where every item has a title and a collection of child items.

The whole view model

Here's how our MoviesPageViewModel.cs ended up looking:

using System.Collections.Generic;
using System.Linq;

namespace WinRT_MVVM_GridView_SemanticZoom
{
    public class MoviesPageViewModel
    {
        public List<MovieCategory> Items { get; set; }

        public MoviesPageViewModel()
        {
            var movies = new List<Movie>
                             {
                                 new Movie {Title = "The Ewok Adventure", Category = "Adventure", Subtitle = "The Towani family civilian shuttlecraft crashes on the forest moon of Endor.", Image = "http://cf2.imgobject.com/t/p/w500/y6HdTlqgcZ6EdsKR1uP03WgBe0C.jpg"},
                                 new Movie {Title = "The In-Laws", Category = "Adventure", Subtitle = "In preparation for his daughter's wedding, dentist Sheldon ", Image = "http://cf2.imgobject.com/t/p/w500/9FlFW9zhuoOpS8frAFR9cCnJ6Sg.jpg"},
                                 new Movie {Title = "The Man Called Flintstone", Category = "Adventure", Subtitle = "In this feature-length film based on the Flintstones TV show", Image = "http://cf2.imgobject.com/t/p/w500/6qyVUkbDBuBOUVVplIDGaQf6jZL.jpg"},

                                 new Movie {Title = "Super Fuzz", Category = "Comedy", Subtitle = "Dave Speed is no ordinary Miami cop--he is an irradiated Miami cop ", Image = "http://cf2.imgobject.com/t/p/w500/bueVXkpCDPX0TlsWd3Uk7QKO3kD.jpg"},
                                 new Movie {Title = "The Knock Out Cop", Category = "Comedy", Subtitle = "This cop doesn't carry a gun - his fist is loaded!", Image = "http://cf2.imgobject.com/t/p/w500/mzlw8rHGUSDobS1MJgz8jXXPM06.jpg"},
                                 new Movie {Title = "Best Worst Movie", Category = "Comedy", Subtitle = "A look at the making of the film Troll 2 (1990) ", Image = "http://cf2.imgobject.com/t/p/w500/5LjbAjkPBUOD9N2QFPSuTyhomx4.jpg"},

                                 new Movie {Title = "The Last Unicorn", Category = "Fantasy", Subtitle = "A brave unicorn and a magician fight an evil king", Image = "http://cf2.imgobject.com/t/p/w500/iO6P5vV1TMwSuisZDtNBDNpOxwR.jpg"},
                                 new Movie {Title = "Blithe Spirit", Category = "Fantasy", Subtitle = "An English mystery novelist invites a medium", Image = "http://cf2.imgobject.com/t/p/w500/gwu4c10lpgHUrMqr9CBNq2FYTpN.jpg"},
                                 new Movie {Title = "Here Comes Mr. Jordan", Category = "Fantasy", Subtitle = "Boxer Joe Pendleton, flying to his next fight", Image = "http://cf2.imgobject.com/t/p/w500/9cnWl7inQVX6wznjYNmQmJXVD6J.jpg"},
                             };

            var moviesByCategories = movies.GroupBy(x => x.Category)
                .Select(x => new MovieCategory { Title = x.Key, Items = x.ToList() });

            Items = moviesByCategories.ToList();
        }
    }

    public class Movie
    {
        public string Title { get; set; }
        public string Subtitle { get; set; }
        public string Image { get; set; }
        public string Category { get; set; }
    }

    public class MovieCategory
    {
        public string Title { get; set; }
        public List<Movie> Items { get; set; }
    }
}

3. Creating the view

imageNow that the view model is ready, we can focus on the view. Let’s start by adding a new page using the “Grouped Items Page” template. We can name the page “Movies.xaml”.  This page template comes with a GridView configured to show grouped data, so it’s an ideal place to start.

Configuring application’s start-up page

After adding the page, we must modify the app.xaml.cs so that the new page will be used as the first page of the application. To do this we can modify the OnLaunched-method:

protected override void OnLaunched(LaunchActivatedEventArgs args) 
{  
    &hellip;     
    if (!rootFrame.Navigate(typeof(Movies)))
    &hellip;
}

Creating the connection between the page and the view model

We now have the page (the view) and the view model. Next step is to create an instance of the view model and set it as the data context of the page. This can be done in the page’s constructor:

public Movies()
        {
            this.InitializeComponent();

            var viewModel = new MoviesPageViewModel();
            this.DataContext = viewModel;
        }

Binding the GridView to a correct property

As you may recall, we named the view model’s public property as “Items”. By default the grouped items page is bound to a property called “Groups”. To change this we can modify the CollectionViewSource declared in the beginning of the Movies.xaml:

<CollectionViewSource
            x:Name="groupedItemsViewSource"
            Source="{Binding Items}"
            IsSourceGrouped="true"
            ItemsPath="Items"/>

Running the application the first time

Now that we have the view and the view model ready, we can run the application the first time:

image

And it actually looks pretty good! You may wonder how the view can display the movie poster, name and the description even though we haven’t created any data bindings between the items and the movies. The reason this works is because the default “Grouped Items Page” template defines the ItemTemplate like this:

image

The “Standard250x250ItemTemplate” is defined inside the CommonStandardStyles.xaml:

<DataTemplate x:Key="Standard250x250ItemTemplate">
        <Grid HorizontalAlignment="Left" Width="250" Height="250">
            <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
                <Image Source="{Binding Image}" Stretch="UniformToFill"/>
            </Border>
            <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                <TextBlock Text="{Binding Title}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
                <TextBlock Text="{Binding Subtitle}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
            </StackPanel>
        </Grid>
    </DataTemplate>

Here’s our Movie-class again:

public class Movie
    {
        public string Title { get; set; }
        public string Subtitle { get; set; }
        public string Image { get; set; }
        public string Category { get; set; }
    }

If you compare the item template against the movie-class, you can see that the template’s binding match the class’ properties.

Now we just need the SemanticZoom.

4. Adding the SemanticZoom –control

Semantic zoom is an UI concept defined by Microsoft like the following:

Semantic Zoom is a touch-optimized technique used by Metro style apps in Windows 8 Consumer Preview for presenting and navigating large sets of related data or content within a single view (such as a photo album, app list, or address book).

SemanticZoom then is a XAML-control which can be used to add the semantic zoom concept into a WinRT application. The SemanticZoom –control requires that we have two representations of the same data: The ZoomedInView and the ZoomedOutView. In our case the ZoomednView is already done because we can use the working GridView. But we need an another List which displays the data when the user zooms out using the Ctrl+Mousewheel. The SemanticZoom only supports the GridView and the ListView controls.

Replacing the ScrollViewer with SemanticZoom

The first and most important thing to do is to completely remove the ScrollViewer named “itemGridScrollViewer” from the Movies.xaml. The SemanticZoom control won’t work correctly if it’s inside the ScrollViewer.

Many people have problems getting the ZoomedOutView and ZoomedInView “in sync”. The usual problem is that when the item in ZoomedOutView is clicked, the ZoomedInView doesn’t scroll to the correct position. The reason for this problem usually is that the SemanticZoom –control is inside a ScrollViewer.

We can then replace the ScrollViewer with a SemanticZoom. Note that the Margin and Grid.Row properties should be copied from the ScrollViewer to the SemanticZoom:

<SemanticZoom x:Name="Zoom" Grid.Row="1" Margin="0,-3,0,0">
     <SemanticZoom.ZoomedInView>     
     </SemanticZoom.ZoomedInView>     
     <SemanticZoom.ZoomedOutView>     
     </SemanticZoom.ZoomedOutView> 
</SemanticZoom>

Creating the ZoomedInView

Creating the ZoomedInView is easy because we’ll already done that. We just need to move the “itemGridView” inside the ZoomedInView-section.

Creating the ZoomedOutView

We don’t have the ZoomedOutView ready but the following GridView can do the trick:

<GridView VerticalAlignment="Center" Margin="200 -200 0 0"> 
    <GridView.ItemTemplate> 
        <DataTemplate> 
            <StackPanel> 
                <TextBlock HorizontalAlignment="Center" 
                           Text="{Binding Group.Title}" 
                           Style="{StaticResource SubheaderTextStyle}" 
                           /> 
            </StackPanel> 
        </DataTemplate> 
    </GridView.ItemTemplate>

    <GridView.ItemContainerStyle> 
        <Style TargetType="GridViewItem"> 
            <Setter Property="Margin" Value="4" /> 
            <Setter Property="Padding" Value="5" /> 
            <Setter Property="HorizontalContentAlignment" Value="Left" /> 
            <Setter Property="VerticalContentAlignment" Value="Center" /> 
        </Style> 
    </GridView.ItemContainerStyle>

    <GridView.ItemsPanel> 
        <ItemsPanelTemplate> 
            <WrapGrid ItemWidth="400" ItemHeight="70" 
                      Orientation="Horizontal" VerticalChildrenAlignment="Center" MaximumRowsOrColumns="3"></WrapGrid> 
        </ItemsPanelTemplate> 
    </GridView.ItemsPanel> 
</GridView>
The most important things to notice:
  • The GridView’s ItemsSource isn’t set in the XAML
  • The TextBox’s data binding is set correctly to “Group.Title”. In this case the Title is bound against the MovieCategory-instances Title-property

The whole SemanticZoom XAML

Overall, this is how the SemanticZoom -control is defined in the XAML:

<SemanticZoom x:Name="Zoom" Grid.Row="1" Margin="0,-3,0,0"> 
    <SemanticZoom.ZoomedInView> 
        <GridView 
        x:Name="itemGridView" 
        AutomationProperties.AutomationId="ItemGridView" 
        AutomationProperties.Name="Grouped Items" 
        Margin="116,0,40,46" 
        ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}" 
        ItemTemplate="{StaticResource Standard250x250ItemTemplate}">

            <GridView.ItemsPanel> 
                <ItemsPanelTemplate> 
                    <VirtualizingStackPanel Orientation="Horizontal"/> 
                </ItemsPanelTemplate> 
            </GridView.ItemsPanel> 
            <GridView.GroupStyle> 
                <GroupStyle> 
                    <GroupStyle.HeaderTemplate> 
                        <DataTemplate> 
                            <Grid Margin="1,0,0,6"> 
                                <Button 
                                AutomationProperties.Name="Group Title" 
                                Content="{Binding Title}" 
                                Style="{StaticResource TextButtonStyle}"/> 
                            </Grid> 
                        </DataTemplate> 
                    </GroupStyle.HeaderTemplate> 
                    <GroupStyle.Panel> 
                        <ItemsPanelTemplate> 
                            <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0"/> 
                        </ItemsPanelTemplate> 
                    </GroupStyle.Panel> 
                </GroupStyle> 
            </GridView.GroupStyle> 
        </GridView> 
    </SemanticZoom.ZoomedInView> 
    <SemanticZoom.ZoomedOutView> 
        <GridView VerticalAlignment="Center" Margin="200 -200 0 0"> 
            <GridView.ItemTemplate> 
                <DataTemplate> 
                    <StackPanel> 
                        <TextBlock HorizontalAlignment="Center" 
                                   Text="{Binding Group.Title}" 
                                   Style="{StaticResource SubheaderTextStyle}" 
                                   /> 
                    </StackPanel> 
                </DataTemplate> 
            </GridView.ItemTemplate>

            <GridView.ItemContainerStyle> 
                <Style TargetType="GridViewItem"> 
                    <Setter Property="Margin" Value="4" /> 
                    <Setter Property="Padding" Value="5" /> 
                    <Setter Property="HorizontalContentAlignment" Value="Left" /> 
                    <Setter Property="VerticalContentAlignment" Value="Center" /> 
                </Style> 
            </GridView.ItemContainerStyle>

            <GridView.ItemsPanel> 
                <ItemsPanelTemplate> 
                    <WrapGrid ItemWidth="400" ItemHeight="70" 
                              Orientation="Horizontal" VerticalChildrenAlignment="Center" MaximumRowsOrColumns="3"></WrapGrid> 
                </ItemsPanelTemplate> 
            </GridView.ItemsPanel> 
        </GridView> 
    </SemanticZoom.ZoomedOutView> 
</SemanticZoom>

 

Configuring the ZoomedOutView’s ItemsSource:

The last step before running the app is to modify the Movies.xaml.cs. In this file we must define how the ListView inside the ZoomedOutView gets its data. We can add the required code to OnNavigatedTo-method:

protected override void OnNavigatedTo(NavigationEventArgs e)

        {
            var collectionGroups = groupedItemsViewSource.View.CollectionGroups;
            ((ListViewBase)this.Zoom.ZoomedOutView).ItemsSource = collectionGroups;
        }

Running the complete application

Our application is ready. The front page looks identical to the previous version:

image

But if you use the Ctrl+Mousewheel, you can see the semantic zoom in action:

image

The source code

This tutorial's source code is available from the GitHub.

Links

39 Comments

Lately I’ve enjoyed working with static web sites. My company’s web pages were previously powered by Wordpress and now they’re just static html pages. There’s few things I especially like with a static web site when compared to other solutions:

  • Performance
  • Hosting options
  • Ease of deployment

Performance

Even though I find the Wordpress near great with all its plugins and themes, the previous web site never felt fast enough. With a static web site you usually don’t have to worry about the performance.

Hosting options

You can host the static site almost everywhere: You don’t need ASP.NET or PHP. You just need a web server like IIS or Apache. Or GitHub.The web server can be run using for example Amazon EC2.

Ease of deployment

In addition to pros mentioned above a static web site is also easy to deploy: There’s no need for a database or configuration. In my case the company web site (html, css and js) is available from a GitHub repository. To host the site on a new web server requires only a Git Clone to the correct directory.

Using Amazon EC2 to host the web site: Automating the deployment

Given that the static web site is available using Git and because we only need a web server to host the site, we can automate the site’s deployment. Here’s how we can do it using Amazon EC2:

1. Launch new instance

Start the Quick Launch Wizard and select Amazon Linux AMI:

image

Amazon Linux supports the configuration of the launch process using cloud-init. Unfortunately I haven’t found any really good examples of using the cloud-init but here’s couple sources: A thread in AWS forums and the following source.

2. Make sure that the instance’s firewall allows the web traffic through

By default the TCP port 80 is blocked. Select a security group which allows the traffic through or create a new security group:

image

3. Input the cloud-init into the “User Data” –field

Here’s a cloud-init initialization code which does the following things:

  1. Installs Apache
  2. Installs Git
  3. Starts the web server
  4. Clones the web site from GitHub into the web server’s root
#cloud-config
packages:
 - httpd
 - git

runcmd:
 - [ /etc/init.d/httpd, restart ]
 - [ git, clone, "-b", "gh-pages", "git://github.com/mikoskinen/softwaremkwebsite.git", "/var/www/html"]

Here’s how the cloud-init initialization code can be set when launching a new Amazon EC2 instance:

 image

And that’s it. After few minutes the Amazon EC2 is ready and hosting the static web site:

image

image

Continuing our exploration of doing navigation in a WinRT XAML Metro-app, there’s one more crucial difference when the page navigation in WinRT is compared to the Windows Phone 7’s page navigation: The navigation cache. In the WP7 world, the page’s life time can be summarized with these three bullets (thanks to Peter Torr):

  • Forward navigation always creates a new instance
  • Pages that are removed from the back stack are released
  • Pages on the back stack are always cached

Contrast this with the WinRT-world where, by default, a new instance of a page is always created. This means that even when you’re navigating back, a new page instance is created.

Testing navigation cache

We can test the navigation cache by using the “Grid Application” template which comes with the Visual Studio 11. Let’s create a new app based on it. The app contains three pages:

  • ItemDetailsPage
  • GroupedItemsPage
  • GroupDetailsPage

Of which the GroupedItemsPage is the landing page of the app. Let’s modify that page’s constructor to write some debug info and then run the app. When navigating forward to GroupDetailsPage and then back, this is the output:

image

Two instances of the same page has been created: One when navigating forward to this page and one when we navigated back to the same page. This is a big difference when compared to the WP7-platform where navigating back always uses the same instance of a page.

Adjusting page cache: NavigationCacheMode

Fortunately we’re able to modify this functionality. Every page has a property called NavigationCacheMode which can be used to change how the navigation caching works on that particular page. The NavigationCacheMode has three possible values. From MSDN:

MemberValueDescription
Disabled0The page is never cached and a new instance of the page is created on each visit.
Required1The page is cached and the cached instance is reused for every visit regardless of the cache size for the frame.
Enabled2The page is cached, but the cached instance is discarded when the size of the cache for the frame is exceeded.

Also the Frame-object, which handles navigation for us, has a property called CacheSize which “Gets or sets the number of pages in the navigation history that can be cached for the frame.”

Testing navigation cache: NavigationCacheMode.Required

Let’s modify GroupedItemsPage so that we set it’s cache mode to “Required”:

<common:LayoutAwarePage
    x:Name="pageRoot"
    x:Class="Application8.GroupedItemsPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Application8"
    xmlns:data="using:Application8.Data"
    xmlns:common="using:Application8.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    NavigationCacheMode="Required"
    mc:Ignorable="d">

And then let’s re-run our previous test. This time only one instance of the page is created:

image

But what happens if we modify the GroupDetailsPage’s (the page where we navigate to) cache mode to Required? After navigating two times forward to the page, we can see that only one instance was created:

image

This means that NavigationCacheMode affects both navigating backward and navigating forward.

How to mimic Windows Phone 7 navigation model

Is it possible to completely mimic the Windows Phone 7 navigation model? I’m not sure at this point. It could be possible if the platform had support for removing a page from it’s navigation history (similar to NavigationService.RemoveBackEntry). Maybe using this functionality in combination with the NavigationCacheMode-property could provide a similar navigation model.

The navigation cache is also an interesting aspect to think of when using the MVVM pattern. With WP7 platform we’ve used to think only about VM’s life time (singleton vs. per request) but with navigation cache we can also tweak the page’s life time. This may open up new opportunities.