Maarten Balliauw {blog}

ASP.NET MVC, Microsoft Azure, PHP, web development ...

NAVIGATION - SEARCH

MvcSiteMapProvider 2.2.0 released

I’m proud to announce that MvcSiteMapProvider 2.2.0 has just been uploaded to CodePlex. It should also be available through NuPack in the coming hours. This release has taken a while, but that’s because I’ve been making some important changes...

MvcSiteMapProvider is, as the name implies, an ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework. Targeted at ASP.NET MVC 2, it provides sitemap XML functionality and interoperability with the classic ASP.NET sitemap controls, like the SiteMapPath control for rendering breadcrumbs and the Menu control.

In this post, I’ll give you a short update on what has changed as well as some examples on how to use newly introduced functionality.

Changes in MvcSiteMapProvider 2.2.0

  • Increased stability
  • HtmlHelpers upgraded to return MvcHtmlString
  • Templated HtmlHelpers have been introduced
  • New extensibility point: OnBeforeAddNode and OnAfterAddNode
  • Optimized sitemap XML for search engine indexing

Templated HtmlHelpers

The MvcSiteMapProvider provides different HtmlHelper extension methods which you can use to generate SiteMap-specific HTML code on your ASP.NET MVC views like a menu, a breadcrumb (sitemap path), a sitemap or just the current node’s title.

All helpers in MvcSiteMapProvider are make use of templates: whenever a node in a menu is to be rendered, a specific partial view is used to render this node. This is based on the idea of templated helpers. The default templates that are used can be found on the downloads page. Locate them under the Views/DisplayTemplates folder of your project to be able to customize them.

When creating your own templates for MvcSiteMapProvider's helpers, the following model objects are used and can be templated:

  • Html.MvcSiteMap().Menu() uses:
    MvcSiteMapProvider.Web.Html.Models.MenuHelperModel and MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
  • Html.MvcSiteMap().SiteMap() uses:
    MvcSiteMapProvider.Web.Html.Models.SiteMapHelperModel and MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
  • Html.MvcSiteMap().SiteMapPath() uses:
    MvcSiteMapProvider.Web.Html.Models.SiteMapPathHelperModel and MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
  • Html.MvcSiteMap().SiteMapTitle()
    MvcSiteMapProvider.Web.Html.Models.Situses:eMapTitleHelperModel

The following template is an example for rendering a sitemap node represented by the MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel model.

1 <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel>" %> 2 <%@ Import Namespace="System.Web.Mvc.Html" %> 3 4 <% if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper") { %> 5 <%=Model.Title %> 6 <% } else if (Model.IsClickable) { %> 7 <a href="<%=Model.Url %>"><%=Model.Title %></a> 8 <% } else { %> 9 <%=Model.Title %> 10 <% } %>

New extensibility point

In the previous release, a number of extensibility points have been introduced. I blogged about them before. A newly introduced extensibility point is the ISiteMapProviderEventHandler . A class implementing MvcSiteMapProvider.Extensibility.ISiteMapProviderEventHandler can be registered to handle specific events, such as when adding a SiteMapNode.

Here’s an example to log all the nodes that are being added to an MVC sitemap:

1 public class MySiteMapProviderEventHandler : ISiteMapProviderEventHandler 2 { 3 public bool OnAddingSiteMapNode(SiteMapProviderEventContext context) 4 { 5 // Should the node be added? Well yes! 6 return true; 7 } 8 9 public void OnAddedSiteMapNode(SiteMapProviderEventContext context) 10 { 11 Trace.Write("Node added: " + context.CurrentNode.Title); 12 } 13 }

Optimized sitemap XML for SEO

Generating a search-engine friendly list of all nodes in a sitemap was already possible. This functionality has been vastly improved with two new features:

  • Whenever a client sends an HTTP request header with Accept-encoding set to a value of gzip or deflate, the XmlSiteMapResult class (which is also used internally in the XmlSiteMapController) will automatically compress the sitemap using GZip compression.
  • Whenever a sitemap exceeds 50.000 nodes, the XmlSiteMapController will automatically split your sitemap into a sitemap index file (sitemap.xml) which references sub-sitemaps (sitemap-1.xml, sitemap-2.xml etc.) as described on http://www.sitemaps.org/protocol.php.

For example, if a website contains more than 50.000 nodes, the sitemap XML that is generated will look similar to the following:

1 <?xml version="1.0" encoding="utf-8" ?> 2 <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 3 <sitemap> 4 <loc>http://localhost:1397/sitemap-1.xml</loc> 5 </sitemap> 6 <sitemap> 7 <loc>http://localhost:1397/sitemap-2.xml</loc> 8 </sitemap> 9 </sitemapindex>

This sitemap index links to sub-sitemap files where all nodes are included.

Scale-out to the cloud, scale back to your rack

That is a bad blog post title, really! If Steve and Ryan have this post in the Cloud Cover show news I bet they will make fun of the title. Anyway…

Imagine you have an application running in your own datacenter. Everything works smoothly, except for some capacity spikes now and then. Someone has asked you for doing something about it with low budget. Not enough budget for new hardware, and frankly new hardware would be ridiculous to just ensure capacity for a few hours each month.

A possible solution would be: migrating the application to the cloud during capacity spikes. Not all the time though: the hardware is in house and you may be a server-hugger that wants to see blinking LAN and HDD lights most of the time. I have to admit: blinking lights are cool! But I digress.

Wouldn’t it be cool to have a Powershell script that you can execute whenever a spike occurs? This script would move everything to Windows Azure. Another script should exist as well, migrating everything back once the spike cools down. Yes, you hear me coming: that’s what this blog post is about.

For those who can not wait, here’s the download: ScaleOutToTheCloud.zip (2.81 kb)

Schematical overview

Since every cool idea goes with fancy pictures, here’s a schematical overview of what could happen when you read this post to the end. First of all: you have a bunch of users making use of your application. As a good administrator, you have deployed IIS Application Request Routing as a load balancer / reverse proxy in front of your application server. Everyone is happy!

IIS Application Request Routing

Unfortunately: sometimes there are just too much users. They keep using the application and the application server catches fire.

Server catches fire!

It is time to do something. Really. Users are getting timeouts and all nasty error messages. Why not run a Powershell script that packages the entire local application for WIndows Azure and deploys the application?

Powershell to the rescue

After deployment and once the application is running in Windows Azure, there’s one thing left for that same script to do: modify ARR and re-route all traffic to Windows Azure instead of that dying server.

Request routing Azure

There you go! All users are happy again, since the application is now running in the cloud one 2, 3, or whatever number of virtual machines.

Let’s try and do this using Powershell…

The Powershell script

The Powershell script will rougly perform 5 tasks:

  • Load settings
  • Load dependencies
  • Build a list of files to deploy
  • Package these files and deploy them
  • Update IIS Application Request Routing servers

Want the download? There you go: ScaleOutToTheCloud.zip (2.81 kb)

Load settings

There are quite some parameters in play for this script. I’ve located them in a settings.ps1 file which looks like this:

# Settings (prod) $global:wwwroot = "C:\inetpub\web.local\" $global:deployProduction = 1 $global:deployDevFabric = 0 $global:webFarmIndex = 0 $global:localUrl = "web.local" $global:localPort = 80 $global:azureUrl = "scaleout-prod.cloudapp.net" $global:azurePort = 80 $global:azureDeployedSite = "http://" + $azureUrl + ":" + $azurePort $global:numberOfInstances = 1 $global:subscriptionId = "" $global:certificate = "C:\Users\Maarten\Desktop\cert.cer" $global:serviceName = "scaleout-prod" $global:storageServiceName = "" $global:slot = "Production" $global:label = Date

Let’s explain these…

$global:wwwroot The file path to the on-premise application.
$global:deployProduction Deploy to Windows Azure?
$global:deployDevFabric Deploy to development fabric?
$global:webFarmIndex The 0-based index of your webfarm. Look at IIS manager and note the order of your web farm in the list of webfarms.
$global:localUrl The on-premise URL that is registered in ARR as the application server.
$global:localPort The on-premise port that is registered in ARR as the application server.
$global:azureUrl The Windows Azure URL that will be registered in ARR as the application server.
$global:azurePort The Windows Azure port that will be registered in ARR as the application server.
$global:azureDeployedSite The final URL of the deployed Windows Azre application.
$global:numberOfInstances Number of instances to run on Windows Azure.
$global:subscriptionId Your Windows Azure subscription ID.
$global:certificate
Your certificate for managing Windows Azure.
$global:serviceName Your Windows Azure service name.
$global:storageServiceName The Windows Azure storage account that will be used for uploading the packaged application.
$global:slot The Windows Azure deployment slot (production/staging)
$global:label The label for the deployment. I chose the current date and time.

Load dependencies

Next, our script will load dependencies. There is one additional set of CmdLets tha tyou have to install: the Windows Azure management CmdLets available at http://code.msdn.microsoft.com/azurecmdlets.

Here’s the set we load:

# Load required CmdLets and assemblies $env:Path = $env:Path + "; c:\Program Files\Windows Azure SDK\v1.2\bin\" Add-PSSnapin AzureManagementToolsSnapIn [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration")

Build a list of files to deploy

In order to package the application, we need a text file containing all the files that should be packaged and deployed to Windows Azure. This is done by recursively traversing the directory where the on-premise application is hosted.

 

$filesToDeploy = Get-ChildItem $wwwroot -recurse | where {$_.extension -match "\..*"} foreach ($fileToDeploy in $filesToDeploy) { $inputPath = $fileToDeploy.FullName $outputPath = $fileToDeploy.FullName.Replace($wwwroot,"") $inputPath + ";" + $outputPath | Out-File FilesToDeploy.txt -Append }

Package these files and deploy them

I have been polite and included this both for development fabric as well as Windows Azure fabric. Here’s the packaging and deployment code for development fabric:

# Package & run the website for Windows Azure (dev fabric) if ($deployDevFabric -eq 1) { trap [Exception] { del -Recurse ScaleOutService continue } cspack ServiceDefinition.csdef /roleFiles:"WebRole;FilesToDeploy.txt" /copyOnly /out:ScaleOutService /generateConfigurationFile:ServiceConfiguration.cscfg # Set instance count (Get-Content ServiceConfiguration.cscfg) | Foreach-Object {$_.Replace("count=""1""","count=""" + $numberOfInstances + """")} | Set-Content ServiceConfiguration.cscfg # Run! csrun ScaleOutService ServiceConfiguration.cscfg /launchBrowser }

And here’s the same for Windows Azure fabric:

# Package the website for Windows Azure (production) if ($deployProduction -eq 1) { cspack ServiceDefinition.csdef /roleFiles:"WebRole;FilesToDeploy.txt" /out:"ScaleOutService.cspkg" /generateConfigurationFile:ServiceConfiguration.cscfg # Set instance count (Get-Content ServiceConfiguration.cscfg) | Foreach-Object {$_.Replace("count=""1""","count=""" + $numberOfInstances + """")} | Set-Content ServiceConfiguration.cscfg # Run! (may take up to 15 minutes!) New-Deployment -SubscriptionId $subscriptionId -certificate $certificate -ServiceName $serviceName -Slot $slot -StorageServiceName $storageServiceName -Package "ScaleOutService.cspkg" -Configuration "ServiceConfiguration.cscfg" -label $label $deployment = Get-Deployment -SubscriptionId $subscriptionId -certificate $certificate -ServiceName $serviceName -Slot $slot do { Start-Sleep -s 10 $deployment = Get-Deployment -SubscriptionId $subscriptionId -certificate $certificate -ServiceName $serviceName -Slot $slot } while ($deployment.Status -ne "Suspended") Set-DeploymentStatus -Status "Running" -SubscriptionId $subscriptionId -certificate $certificate -ServiceName $serviceName -Slot $slot $wc = new-object system.net.webclient $html = "" do { Start-Sleep -s 60 trap [Exception] { continue } $html = $wc.DownloadString($azureDeployedSite) } while (!$html.ToLower().Contains("<html")) }

Update IIS Application Request Routing servers

This one can be done by abusing the .NET class Microsoft.Web.Administration.ServerManager.

# Modify IIS ARR $mgr = new-object Microsoft.Web.Administration.ServerManager $conf = $mgr.GetApplicationHostConfiguration() $section = $conf.GetSection("webFarms") $webFarms = $section.GetCollection() $webFarm = $webFarms[$webFarmIndex] $servers = $webFarm.GetCollection() $server = $servers[0] $server.SetAttributeValue("address", $azureUrl) $server.ChildElements["applicationRequestRouting"].SetAttributeValue("httpPort", $azurePort) $mgr.CommitChanges()

Running the script

Of course I’ve tested this to see if it works. And guess what: it does!

The script output itself is not very interesting. I did not add logging or meaningful messages to see what it is doing. Instead you’ll just see it working.

Powershell script running

Once it has been fired up, the Windows Azure portal will soon be showing that the application is actually deploying. No hands!

Powershell deployment to Azure

After the usual 15-20 minutes that a deployment + application first start takes, IIS ARR is re-configured by Powershell.

image

And my local users can just keep browsing to http://farm.local which now simply routes requests to Windows Azure. Don’t be fooled: I actually just packaged the default IIS website and deployed it to Windows Azure. Very performant!

image

Conclusion

It works! And it’s fancy and cool stuff. I think this may be a good deployment and scale-out model in some situations, however there may still be a bottleneck in the on-premise ARR server: if this one has too much traffic to cope with, a new burning server is in play. Note that this solution will work for any website hosted on IIS: custom made ASP.NET apps, ASP.NET MVC, PHP, …

Here’s the download: ScaleOutToTheCloud.zip (2.81 kb)

Windows Phone 7 First Impressions

Windows Phone 7Back in june of this year, I received a very surprising e-mail stating that I would receive a Windows Phone 7 developer device. The reason for this? No, not that I’m handsome. But the fact that I paid $99 for listing an application in the marketplace that they were hoping me to port to Windows Phone 7. The wait continued: july? No phone. August? No phone. By september I thought I was not getting a Windows Phone 7 anymore. Until this week: another e-mail stating that the device was shipped. And today, FedEx kindly handed me over a developer device.

After installing my SIM card and starting the phone, I was welcomed by the nice looking Windows Phone 7 tiles. And that is where the rest of my journey started…

Disclaimer: This device is not a production device. It is a prototype developed by LG and I have absolutely no clue how this thing will look like and perform in the production model. Next, I have only used this phone for a few hours yet. Therefore do not base final judgements of the product on this blog post.

By the way, seems like Scott Hanselman also received a prototype phone and blogged about his experience.

Things I like

Here’s a list of the things I really, really like:

  • Windows Live, Google, Yahoo and Exchange are data sources for contacts and calendar information. I added my Windows Live account, Facebook, GMail and work Exchange account and WP7 is happily synchronizing everything.
  • With all that synching, WP7 seems to identify contacts: my wife’s Windows Live contact details, Facebook details and GMail details are all combined into one contact on the phone which is extremely useful. All contact information is centralized in that way.
  • The photo camera (5 MP) has a dedicated button. A button that also lets you take pictures if the phone is turned off! No need for a 20 seconds boot time if you want to quickly take a picture: just push the button and take a picture.
  • The tiles on the main screen are a very refreshing UI concept. Also, the UI is very smooth and fast compared with any other phone I used in the past.
  • Everything is very straightforward: you can get up and running in no time.
  • This model has a hardware keyboard which is a nice addition. Typing using the on-screen keyboard works but I do have thick fingers that are wrong sometimes. No problem in English as the English dictionary is helping as you type, but there’s no Dutch dictionary in this one yet which gives me a lot of mistakes. Unless I use the hardware keyboard, which I like better than on-screen.

Things I miss

There are some things I miss and may have me ending up with 2 phones in my pocket: my WP6 and WP7 phone. Here’s a list of things I miss:

  • A decent Twitter application. There's an app for that but not yet something like PockeTwit which I used on WP6.
  • Facebook is nicely integrated, except I can not find a place to update my status. Writing on people’s wall and commenting on people’s posts is possible but I seem to be missing a place where I can just enter “What’s on my mind?” Found here how it works.
  • There’s no such thing as Outlook synchronization! I have a GMail account for e-mail and tend to maintain contacts and calendar in good old Outlook. After looking at how to synchronize these I haven’t found a good manner to synchronize these contacts and calendar. My current solution was a bulk import into Windows Live contacts and calendar which works but is not very straightforward if you are used to just ActiveSyncing everything. Actually I started liking Windows Live for this matter. So maybe you'll miss it but I no longer do.
  • I have TomTom on my WP6 phone, does anyone know if they will release a WP7 version that does not eat your data plan?
  • Tethering! Really, every modern phone supports this!

Things I dislike

Another list of items… I dislike:

  • I don’t really like the Zune software. It appears very bloated and requires a lot of clicking to get some basic stuff done.
  • Fortunately I have a data plan with my cellphone operator. If you do not have such a thing, the WP7 will cost you a lot of money. If you would turn off data on the device, it will have less functionality.
  • There’s no Outlook synchronization. I fixed this with a workaround (see “Things I miss”), but would rather see this supported out-of-the-box.
  • Battery life seems rather short (+/ 1,5 day before having to recharge?)
  • No tethering???

Conclusion

If you look at the lists above, you will notice that I like the device and OS. There’s some lacking functionality and apps, but I’m sure these will be available soon after the release. It’s very surprising how smooth this device works and how easy it is to work with. Looking forward to the first official devices! And hope they will sync with Outlook and will support tethering...

Using MvcSiteMapProvider throuh NuPack

NuPackProbably you have seen the buzz around NuPack, a package manager for .NET with thight integration in Visual Studio 2010. NuPack is a free, open source developer focused package management system for the .NET platform intent on simplifying the process of incorporating third party libraries into a .NET application during development. If you download and install NuPack into Visual Studio, you can now reference MvcSiteMapProvider with a few simple clicks!

From within your ASP.NET MVC 2 project, right click the project file and use the new “Add Package Reference…” option.

Add package reference

Next, a nice dialog shows up where you can just pick a package and click “Install” to download it and add the necessary references to your project. The packages are retrieved from a central XML feed, but feel free to add a reference to a directory where your corporate packages are stored and install them through NuPack. Anyway: MvcSiteMapProvider. Just look for it in the list and click “Install”.

MvcSiteMapProvider in NuPack

Next, MvcSiteMapProvider will automatically be downloaded, added as an assembly reference, a default Mvc.sitemap file is added to your project and all configuration in Web.config takes place without having to do anything! I’m sold :-)

Disclaimer for some: I’m not saying NuPack is the best package manager out there nor that it is the best original idea ever invented. I do believe that the tight integration in VS2010 will make NuPack a good friend during development: the process of downloading and including third party components in your application becomes frictionless. That’s the aim for NuPack, and also the reason why I believe this tool matters and will matter a lot!

Cost Architecting for Windows Azure

Cost architecting for Windows AzureJust wanted to do a quick plug to an article I’ve written for TechNet Magazine: Windows Azure: Cost Architecting for Windows Azure.

Designing applications and solutions for cloud computing and Windows Azure requires a completely different way of considering the operating costs.

Cloud computing and platforms like Windows Azure are billed as “the next big thing” in IT. This certainly seems true when you consider the myriad advantages to cloud computing.

Computing and storage become an on-demand story that you can use at any time, paying only for what you effectively use. However, this also poses a problem. If a cloud application is designed like a regular application, chances are that that application’s cost perspective will not be as expected.

Want to read more? Check the full article. I will also be doing a session on this later this month for the Belgian Azure User Group.