Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Azure, PHP, OpenXML, VSTS, ...

About the author

Maarten Balliauw is currently employed as .NET Technical Consultant at RealDolmen. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
View Maarten Balliauw's MVP profile

Search

Latest Twitter

    Follow me on Twitter...

    My projects

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    © Copyright Maarten Balliauw 2010

    Hybrid Azure applications using OData

    OData in the cloud on AzureIn the whole Windows Azure story, Microsoft has always been telling you could build hybrid applications: an on-premise application with a service on Azure or a database on SQL Azure. But how to do it in the opposite direction? Easy answer there: use the (careful, long product name coming!) Windows Azure platform AppFabric Service Bus to expose an on-premise WCF service securely to an application hosted on Windows Azure. Now how would you go about exposing your database to Windows Azure? Open a hole in the firewall? Use something like PortBridge to redirect TCP traffic over the service bus? Why not just create an OData service for our database and expose that over AppFabric Service Bus. In this post, I’ll show you how.

    For those who can not wait: download the sample code: ServiceBusHost.zip (7.87 kb)

    kick it on DotNetKicks.com

    What we are trying to achieve

    The objective is quite easy: we want to expose our database to an application on Windows Azure (or another cloud or in another datacenter) without having to open up our firewall. To do so, we’ll be using an OData service which we’ll expose through Windows Azure platform AppFabric Service Bus. But first things first…

    • OData??? The Open Data Protocol (OData) is a Web protocol for querying and updating data over HTTP using REST. And data can be a broad subject: a database, a filesystem, customer information from SAP, … The idea is to have one protocol for different datasources, accessible through web standards. More info? Here you go.
    • Service Bus??? There’s an easy explanation to this one, although the product itself offers much more use cases. We’ll be using the Service Bus to interconnect two applications, sitting on different networks and protected by firewalls. This can be done by using the Service Bus as the “man in the middle”, passing around data from both applications.

    Windows Azure platform AppFabric Service Bus

    Our OData feed will be created using WCF Data Services, formerly known as ADO.NET Data Services formerly known as project Astoria.

    Creating an OData service

    We’ll go a little bit off the standard path to achieve this, although the concepts are the same. Usually, you’ll be adding an OData service to a web application in Visual Studio. Difference: we’ll be creating a console application. So start off with a console application and add the following additional references to the console application:

    • Microsoft.ServiceBus (from the SDK that can be found on the product site)
    • System.Data.Entity
    • System.Data.Services
    • System.Data.Services.Client
    • System.EnterpriseServices
    • System.Runtime.Serialization
    • System.ServiceModel
    • System.ServiceModel.Web
    • System.Web.ApplicationServices
    • System.Web.DynamicData
    • System.Web.Entity
    • System.Web.Services
    • System.Data.DataSetExtensions

    Next, add an Entity Data Model for a database you want to expose. I have a light version of the Contoso sample database and will be using that one. Also, I only added one table to the model for sake of simplicity:

    Entity Data Model for OData

    Pretty straightforward, right? Next thing: expose this beauty through an OData service created with WCF Data Services. Add a new class to the project, and add the following source code:

    1 public class ContosoService : DataService<ContosoSalesEntities> 2 { 3 // This method is called only once to initialize service-wide policies. 4 public static void InitializeService(DataServiceConfiguration config) 5 { 6 // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. 7 // Examples: 8 // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead); 9 // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All); 10 config.SetEntitySetAccessRule("Store", EntitySetRights.All); 11 config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2; 12 } 13 }

    Let’s explain this thing: the ContosoService class inherits DataService<ContosoSalesEntities>, a ready-to-use service implementation which you pass the type of your Entity Data Model. In the InitializeService method, there’s only one thing left to do: specify the access rules for entities. I chose to expose the entity set “Store” with all rights (read/write).

    In a normal world: this would be it: we would now have a service ready to expose our database through OData. Quick, simple, flexible. But in our console application, there’s a small problem: we are not hosting inside a web application, so we’ll have to write the WCF hosting code ourselves.

    Hosting the OData service using a custom WCF host

    Since we’re not hosting inside a web application but in a console application, there’s some plumbing we need to do: set up our own WCF host and configure it accordingly.

    Let’s first work on our App.config file:

    1 <?xml version="1.0"?> 2 <configuration> 3 <connectionStrings> 4 <add name="ContosoSalesEntities" connectionString="metadata=res://*/ContosoModel.csdl|res://*/ContosoModel.ssdl|res://*/ContosoModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.\SQLEXPRESS;Initial Catalog=ContosoSales;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient"/> 5 </connectionStrings> 6 7 <system.serviceModel> 8 <services> 9 <service behaviorConfiguration="contosoServiceBehavior" 10 name="ServiceBusHost.ContosoService"> 11 <host> 12 <baseAddresses> 13 <add baseAddress="http://localhost:8080/ContosoModel" /> 14 </baseAddresses> 15 </host> 16 <endpoint address="" 17 binding="webHttpBinding" 18 contract="System.Data.Services.IRequestHandler" /> 19 20 <endpoint address="https://<service_namespace>.servicebus.windows.net/ContosoModel/" 21 binding="webHttpRelayBinding" 22 bindingConfiguration="contosoServiceConfiguration" 23 contract="System.Data.Services.IRequestHandler" 24 behaviorConfiguration="serviceBusCredentialBehavior" /> 25 </service> 26 </services> 27 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 28 29 <behaviors> 30 <serviceBehaviors> 31 <behavior name="contosoServiceBehavior"> 32 <serviceMetadata httpGetEnabled="true" /> 33 <serviceDebug includeExceptionDetailInFaults="True" /> 34 </behavior> 35 </serviceBehaviors> 36 37 <endpointBehaviors> 38 <behavior name="serviceBusCredentialBehavior"> 39 <transportClientEndpointBehavior credentialType="SharedSecret"> 40 <clientCredentials> 41 <sharedSecret issuerName="owner" issuerSecret="<secret_from_portal>" /> 42 </clientCredentials> 43 </transportClientEndpointBehavior> 44 </behavior> 45 </endpointBehaviors> 46 </behaviors> 47 48 <bindings> 49 <webHttpRelayBinding> 50 <binding name="contosoServiceConfiguration"> 51 <security relayClientAuthenticationType="None" /> 52 </binding> 53 </webHttpRelayBinding> 54 </bindings> 55 </system.serviceModel> 56 </configuration>

    There's a lot of stuff going on in there!

    • The connection string to my on-premise database is specified
    • The WCF service is configured

    To be honest: that second bullet is a bunch of work…

    • We specify 2 endpoints: one local (so we can access the OData service from our local network) and one on the service bus, hence the https://<service_namespace>.servicebus.windows.net/ContosoModel/ URL.
    • The service bus endpoint has 2 behaviors specified: the service behavior is configured to allow metadata retrieval. The endpoint behavior is configured to use the service bus credentials (that can be found on the AppFabric portal site once logged in) when connecting to the service bus.
    • The webHttpRelayBinding, a new binding type for Windows Azure AppFabric Service Bus, is configured to use no authentication when someone connects to it. That way, we will have an OData service that is accessible from the Internet for anyone.

    With that configuration in place, we can start building our WCF service host in code. Here’s the full blown snippet:

    1 class Program 2 { 3 static void Main(string[] args) 4 { 5 ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.AutoDetect; 6 7 using (ServiceHost serviceHost = new WebServiceHost(typeof(ContosoService))) 8 { 9 try 10 { 11 // Open the ServiceHost to start listening for messages. 12 serviceHost.Open(TimeSpan.FromSeconds(30)); 13 14 // The service can now be accessed. 15 Console.WriteLine("The service is ready."); 16 foreach (var endpoint in serviceHost.Description.Endpoints) 17 { 18 Console.WriteLine(" - " + endpoint.Address.Uri); 19 } 20 Console.WriteLine("Press <ENTER> to terminate service."); 21 Console.ReadLine(); 22 23 // Close the ServiceHost. 24 serviceHost.Close(); 25 } 26 catch (TimeoutException timeProblem) 27 { 28 Console.WriteLine(timeProblem.Message); 29 Console.ReadLine(); 30 } 31 catch (CommunicationException commProblem) 32 { 33 Console.WriteLine(commProblem.Message); 34 Console.ReadLine(); 35 } 36 } 37 } 38 }

    We’ve just created our hosting environment, completely configured using the configuration file for WCF. The important thing to note here is that we’re spinning up a WebServiceHost, and that we’re using it to host multiple endpoints. Compile, run, F5, and here’s what happens:

    Command line WCF hosting for AppFabric service bus

    Consuming the feed

    Now just leave that host running and browse to the public service bus endpoint for your OData service, i.e. https://<service_namespace>.servicebus.windows.net/ContosoModel/:

    Consuming OData over service bus

    There’s two reactions possible now: “So, this is a service?” and “WOW! I actually connected to my local SQL Server database using a public URL and I did not have to call IT to open up the firewall!”. I’d go for the latter…

    Of course, you can also consume the feed from code. Open up a new project in Visual Studio, and add a service reference for the public service bus address:

    Add reference OData

    The only thing left now is consuming it, for example using this code snippet:

    1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var odataService = 6 new ContosoSalesEntities( 7 new Uri("https://<service_namespace>.servicebus.windows.net/ContosoModel/")); 8 var store = odataService.Store.Take(1).ToList().First(); 9 10 Console.WriteLine("Store: " + store.StoreName); 11 Console.ReadLine(); 12 } 13 }

    (Do not that updates do not work out-of-the-box, you’ll have to use a small portion of magic on the server side to fix that… I’ll try to follow up on that one.)

    Conclusion

    That was quite easy! Of course, if you need full access to your database, you are currently stuck with PortBridge or similar solutions. I am not completely exposing my database to the outside world: there’s an extra level of control in the EDMX model where I can choose which datasets to expose and which not. The WCF Data Services class I created allows for specifying user access rights per dataset.

    Download sample code: ServiceBusHost.zip (7.87 kb)

    kick it on DotNetKicks.com


    Categories: Azure | C# | General

    Simplified access control using Windows Azure AppFabric Labs

    Windows Azure ApFabric Access ControlEarlier this week, Zane Adam announced the availability of the New AppFabric Access Control service in LABS. The highlights for this release (and I quote):

    • Expanded Identity provider support - allowing developers to build applications and services that accept both enterprise identities (through integration with Active Directory Federation Services 2.0), and a broad range of web identities (through support of Windows Live ID, Open ID, Google, Yahoo, Facebook identities) using a single code base.
    • WS-Trust and WS-Federation protocol support – Interoperable WS-* support is important to many of our enterprise customers.
    • Full integration with Windows Identity Foundation (WIF) - developers can apply the familiar WIF identity programming model and tooling for cloud applications and services.
    • A new management web portal -  gives simple, complete control over all Access Control settings.

    Wow! This just *has* to be good! Let’s see how easy it is to work with claims based authentication and the AppFabric Labs Access Control Service, which I’ll abbreviate to “ACS” throughout this post.

    kick it on DotNetKicks.com

    What are you doing?

    In essence, I’ll be “outsourcing” the access control part of my application to the ACS. When a user comes to the application, he will be asked to present certain “claims”, for example a claim that tells what the user’s role is. Of course, the application will only trust claims that have been signed by a trusted party, which in this case will be the ACS.

    Fun thing is: my application only has to know about the ACS. As an administrator, I can then tell the ACS to trust claims provided by Windows Live ID or Google Accounts, which will be reflected to my application automatically: users will be able to authenticate through any service I configure in the ACS, without my application having to know. Very flexible, as I can tell the ACS to trust for example my company’s Active Directory and perhaps also the Active Directory of a customer who uses the application

    Prerequisites

    Before you start, make sure you have the latest version of Windows Identity Foundation installed. This will make things easy, I promise! Other prerequisites, of course, are Visual Studio and an account on https://portal.appfabriclabs.com. Note that, since it’s still a “preview” version, this is free to use.

    In the labs account, create a project and in that project create a service namespace. This is what you should be seeing (or at least: something similar):

    AppFabric labs project

    Getting started: setting up the application side

    Before starting, we will require a certificate for signing tokens and things like that. Let’s just start with creating one so we don’t have to worry about that further down the road. Issue the following command in a Visual Studio command prompt:

    MakeCert.exe -r -pe -n "CN=<your service namespace>.accesscontrol.appfabriclabs.com" -sky exchange -ss my

    This will create a certificate that is valid for your ACS project. It will be installed in the local certificate store on your computer. Make sure to export both the public and private key (.cer and .pkx).

    That being said and done: let’s add claims-based authentication to a new ASP.NET Website. Simply fire up Visual Studio, create a new ASP.NET application. I called it “MyExternalApp” but in fact the name is all up to you. Next, edit the Default.aspx page and paste in the following code:

    1 <%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" 2 CodeBehind="Default.aspx.cs" Inherits="MyExternalApp._Default" %> 3 4 <asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"> 5 </asp:Content> 6 <asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent"> 7 <p>Your claims:</p> 8 <asp:GridView ID="gridView" runat="server" AutoGenerateColumns="False"> 9 <Columns> 10 <asp:BoundField DataField="ClaimType" HeaderText="ClaimType" ReadOnly="True" /> 11 <asp:BoundField DataField="Value" HeaderText="Value" ReadOnly="True" /> 12 </Columns> 13 </asp:GridView> 14 </asp:Content>

    Next, edit Default.aspx.cs and add the following Page_Load event handler:

    1 protected void Page_Load(object sender, EventArgs e) 2 { 3 IClaimsIdentity claimsIdentity = 4 ((IClaimsPrincipal)(Thread.CurrentPrincipal)).Identities.FirstOrDefault(); 5 6 if (claimsIdentity != null) 7 { 8 gridView.DataSource = claimsIdentity.Claims; 9 gridView.DataBind(); 10 } 11 }

    So far, so good. If we had everything configured, Default.aspx would simply show us the claims we received from ACS once we have everything running. Now in order to configure the application to use the ACS, there’s two steps left to do:

    • Add a reference to Microsoft.IdentityModel (located somewhere at C:\Program Files\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5\Microsoft.IdentityModel.dll)
    • Add an STS reference…

    That first step should be easy: add a reference to Microsoft.IdentityModel in your ASP.NET application. The second step is almost equally easy: right-click the project and select “Add STS reference…”, like so:

    Add STS reference

    A wizard will pop-up. Here’s a secret: this wizard will do a lot for us! On the first screen, enter the full URL to your application. I have mine hosted on IIS and enabled SSL, hence the following screenshot:

    Specify application URI

    In the next step, enter the URL to the STS federation metadata. To the what where? Well, to the metadata provided by ACS. This metadata contains the types of claims offered, the certificates used for signing, … The URL to enter will be something like https://<your service namespace>.accesscontrol.appfabriclabs.com:443/FederationMetadata/2007-06/FederationMetadata.xml:

    Security Token Service

    In the next step, select “Disable security chain validation”. Because we are using self-signed certificates, selecting the second option would lead us to doom because all infrastructure would require a certificate provided by a valid certificate authority.

    From now on, it’s just “Next”, “Next”, “Finish”. If you now have a look at your Web.config file, you’ll see that the wizard has configured the application to use ACS as the federation authentication provider. Furthermore, a new folder called “FederationMetadata” has been created, which contains an XML file that specifies which claims this application requires. Oh, and some other details on the application, but nothing to worry about at this point.

    Our application has now been configured: off to the ACS side!

    Getting started: setting up the ACS side

    First of all, we need to register our application with the Windows Azure AppFabric ACS. his can be done by clicking “Manage” on the management portal over at https://portal.appfabriclabs.com. Next, click “Relying Party Applications” and “Add Relying Party Application”. The following screen will be presented:

    Add Relying Party Application

    Fill out the form as follows:

    • Name: a descriptive name for your application.
    • Realm: the URI that the issued token will be valid for. This can be a complete domain (i.e. www.example.com) or the full path to your application. For now, enter the full URL to your application, which will be something like https://localhost/MyApp.
    • Return URL: where to return after successful sign-in
    • Token format: we’ll be using the defaults in WIF, so go for SAML 2.0.
    • For the token encryption certificate, select X.509 certificate and upload the certificate file (.cer) we’ve been using before
    • Rule groups: pick one, best is to create a new one specific to the application we are registering

    Afterwards click “Save”. Your application is now registered with ACS.

    The next step is to select the Identity Providers we want to use. I selected Windows Live ID and Google Accounts as shown in the next screenshot:

    Identity Providers

    One thing left: since we are using Windows Identity Foundation, we have to upload a token signing certificate to the portal. Export the private key of the previously created certificate and upload that to the “Certificates and Keys” part of the management portal. Make sure to specify that the certificate is to be used for token signing.

    Signing certificate Windows Identity Foundation WIF

    Allright, we’re almost done. Well, in fact: we are done! An optional next step would be to edit the rule group we’ve created before. This rule group will describe the claims that will be presented to the application asking for the user’s claims. Which is very powerful, because it also supports so-called claim transformations: if an identity provider provides ACS with a claim that says “the user is part of a group named Administrators”, the rules can then transform the claim into a new claim stating “the user has administrative rights”.

    Testing our setup

    With all this information and configuration in place, press F5 inside Visual Studio and behold… Your application now redirects to the STS in the form of ACS’ login page.

    Sign in using AppFabric

    So far so good. Now sign in using one of the identity providers listed. After a successful sign-in, you will be redirected back to ACS, which will in turn redirect you back to your application. And then: misery :-)

    Request validation

    ASP.NET request validation kicked in since it detected unusual headers. Let’s fix that. Two possible approaches:

    • Disable request validation, but I’d prefer not to do that
    • Create a custom RequestValidator

    Let’s go with the latter option… Here’s a class that you can copy-paste in your application:

    1 public class WifRequestValidator : RequestValidator 2 { 3 protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) 4 { 5 validationFailureIndex = 0; 6 7 if (requestValidationSource == RequestValidationSource.Form && collectionKey.Equals(WSFederationConstants.Parameters.Result, StringComparison.Ordinal)) 8 { 9 SignInResponseMessage message = WSFederationMessage.CreateFromFormPost(context.Request) as SignInResponseMessage; 10 11 if (message != null) 12 { 13 return true; 14 } 15 } 16 17 return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex); 18 } 19 }

    Basically, it’s just validating the request and returning true to ASP.NET request validation if a SignInMesage is in the request. One thing left to do: register this provider with ASP.NET. Add the following line of code in the <system.web> section of Web.config:

    <httpRuntime requestValidationType="MyExternalApp.Modules.WifRequestValidator" />

    If you now try loading the application again, chances are you will actually see claims provided by ACS:

    Claims output from Windows Azure AppFabric Access Control Service

    There', that’s it. We now have successfully delegated access control to ACS. Obviously the next step would be to specify which claims are required for specific actions in your application, provide the necessary claims transformations in ACS, … All of that can easily be found on Google Bing.

    Conclusion

    To be honest: I’ve always found claims-based authentication and Windows Azure AppFabric Access Control a good match in theory, but an ugly and cumbersome beast to work with. With this labs release, things get interesting and almost self-explaining, allowing for easier implementation of it in your own application. As an extra bonus to this blog post, I also decided to link my ADFS server to ACS: it took me literally 5 minutes to do so and works like a charm!

    Final conclusion: AppFabric team, please ship this soon :-) I really like the way this labs release works and I think many users who find the step up to using ACS today may as well take the step if they can use ACS in the easy manner the labs release provides.

    By the way: more information can be found on http://acs.codeplex.com.

    kick it on DotNetKicks.com


    Categories: Azure | C# | General | Security

    MvcSiteMapProvider 2.1.0 released!

    MvcSiteMapProvider The release for MvcSiteMapProvider 2.1.0 has just been posted on CodePlex. 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.

    Next to a brand new logo, the component has been patched up with several bugfixes, the visibility attribute is back (in a slightly cooler reincarnation) and a number of new extension points have been introduced. Let’s give you a quick overview…

    kick it on DotNetKicks.com

    Extension points

    MvcSiteMapProvider is built wih extensibility in mind. All extension point contracts are defined in the MvcSiteMapProvider.Extensibility namespace. The sample application on the downloads page contains several custom implementations of these extension points.

    Global extension points (valid for the entire provider and all nodes)

    These extension points can be defined when Registering the provider.

      Node key generator

    Keys for sitemap nodes are usually automatically generated by the MvcSiteMapProvider core. If, for reasons of accessing sitemap nodes from code, the generated keys should follow other naming rules, a custom MvcSiteMapProvider.Extensibility.INodeKeyGenerator implementation can be written.

      Controller type resolver

    In order to resolve a controller type and action method related to a specific sitemap node, a MvcSiteMapProvider.Extensibility.IControllerTypeResolver is used. This should normally not be extended, however if you want to make use of other systems for resolving controller types and action methods, this is the logical extension point.

      Action method parameter resolver

    Action method parameters are resolved by using ASP.NET MVC's ActionDescriptor class. If you want to use a custom system for this, a MvcSiteMapProvider.Extensibility.IActionMethodParameterResolver implementation can be specified.

      ACL module

    To determine whether a sitemap node is accessible to a specific user, a MvcSiteMapProvider.Extensibility.IAclModule implementation is used. MvcSiteMapProvider uses two of these modules by default: access is granted or denied by checking for [Authorize] attributes on action methods, followed by the roles attribute that can be specified in the sitemap XML.

      URL resolver

    URLs are generated by leveraging a MvcSiteMapProvider.Extensibility.ISiteMapNodeUrlResolver implementation. If, for example, you want all URLs generated by MvcSiteMapProvider to be in lowercase text, a custom implementation can be created.

      Visibility provider

    In some situations, nodes should be visible in the breadcrumb trail but not in a complete sitemap. This can be solved using the MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider extension point that can be specified globally for every node in the sitemap or granularly on a specific sitemap node. A sample is available on the Advanced node visibility page.

    Node-specific extenion points (valid for a single node)

    These extension points can be defined when Creating a first sitemap.

      Dynamic node provider

    In many web applications, sitemap nodes are directly related to content in a persistent store like a database.For example, in an e-commerce application, a list of product details pages in the sitemap maps directly to the list of products in the database. Using dynamic sitemaps, a small class implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider or extending MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase can be provided to the MvcSiteMapProvider offering a list of dynamic nodes that should be incldued in the sitemap. This ensures the product pages do not have to be specified by hand in the sitemap XML.

    A sample can be found on the Dynamic sitemaps page.

      URL resolver

    URLs are generated by leveraging a MvcSiteMapProvider.Extensibility.ISiteMapNodeUrlResolver implementation. If, for example, you want the URL for a sitemap node generated by MvcSiteMapProvider to be in lowercase text, a custom implementation can be created.

      Visibility provider

    In some situations, nodes should be visible in the breadcrumb trail but not in a complete sitemap. This can be solved using the MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider extension point that can be specified globally for every node in the sitemap or granularly on a specific sitemap node. A sample is available on the Advanced node visibility page.

    Conclusion

    Only one conclusion: grab the latest bits and start playing with them! And feel free to bug me with feature requests and issues found.

    Also, follow me on Twitter for updates on this project.


    Categories: ASP.NET | C# | General | MVC | Projects

    ASP.NET MVC 3 and MEF sitting in a tree...

    As I stated in a previous blog post: ASP.NET MVC 3 preview 1 has been released! I talked about some of the new features and promised to do a blog post in the dependency injection part. In this post, I'll show you how to use that together with MEF.

    Download my sample code: Mvc3WithMEF.zip (256.21 kb)

    kick it on DotNetKicks.com

    Dependency injection in ASP.NET MVC 3

    First of all, there’s 4 new hooks for injecting dependencies:

    • When creating controller factories
    • When creating controllers
    • When creating views (might be interesting!)
    • When using action filters

    In ASP.NET MVC 2, only one of these hooks was used for dependency injection: a controller factory was implemented, using a dependency injection framework under the covers. I did this once, creating a controller factory that wired up MEF and made sure everything in the application was composed through a MEF container. That is, everything that is a controller or part thereof. No easy options for DI-ing things like action filters or views…

    ASP.NET MVC 3 shuffled the cards a bit. ASP.NET MVC 3 now contains and uses the Common Service Locator’s IServiceLocator interface, which is used for resolving services required by the ASP.NET MVC framework. The IServiceLocator implementation should be registered in Global.asax using just one line of code:

    MvcServiceLocator.SetCurrent(new SomeServiceLocator());

    This is, since ASP.NET MVC 3 preview 1, the only thing required to make DI work. In controllers, in action filters and in views. Cool, eh?

    Leveraging MEF with ASP.NET MVC 3

    First of all: a disclaimer. I already did posts on MEF and ASP.NET MVC before, and in all these posts, I required you to explicitly export your controller types for composition. In this example, again, I will require that, just for keeping code a bit easier to understand. Do note that are some variants of a convention based registration model available.

    As stated before, the only thing to build here is a MefServiceLocator that is suited for web (which means: an application-wide catalog and a per-request container). I’ll still have to create my own controller factory as well, because otherwise I would not be able to dynamically compose my controllers. Here goes…

    Implementing ServiceLocatorControllerFactory

    Starting in reverse, but this thing is the simple part :-)

    [Export(typeof(IControllerFactory))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class ServiceLocatorControllerFactory
        : DefaultControllerFactory
    {
        private IMvcServiceLocator serviceLocator;

        [ImportingConstructor]
        public ServiceLocatorControllerFactory(IMvcServiceLocator serviceLocator)
        {
            this.serviceLocator = serviceLocator;
        }

        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            var controllerType = GetControllerType(requestContext, controllerName);
            if (controllerType != null)
            {
                return this.serviceLocator.GetInstance(controllerType) as IController;
            }

            return base.CreateController(requestContext, controllerName);
        }

        public override void ReleaseController(IController controller)
        {
            this.serviceLocator.Release(controller);
        }
    }

    Did you see that? A simple, MEF enabled controller factory that uses an IMvcServiceLocator. This thing can be used with other service locators as well.

    Implementing MefServiceLocator

    Like I said, this is the most important part, allowing us to use MEF for resolving almost any component in the ASP.NET MVC pipeline. Here’s my take on that:

    [Export(typeof(IMvcServiceLocator))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class MefServiceLocator
        : IMvcServiceLocator
    {
        const string HttpContextKey = "__MefServiceLocator_Container";

        private ComposablePartCatalog catalog;
        private IMvcServiceLocator defaultLocator;

        [ImportingConstructor]
        public MefServiceLocator()
        {
            // Get the catalog from the MvcServiceLocator.
            // This is a bit dirty, but currently
            // the only way to ensure one application-wide catalog
            // and a per-request container.
            MefServiceLocator mefServiceLocator = MvcServiceLocator.Current as MefServiceLocator;
            if (mefServiceLocator != null)
            {
                this.catalog = mefServiceLocator.catalog;
            }

            // And the fallback locator...
            this.defaultLocator = MvcServiceLocator.Default;
        }

        public MefServiceLocator(ComposablePartCatalog catalog)
            : this(catalog, MvcServiceLocator.Default)
        {
        }

        public MefServiceLocator(ComposablePartCatalog catalog, IMvcServiceLocator defaultLocator)
        {
            this.catalog = catalog;
            this.defaultLocator = defaultLocator;
        }

        protected CompositionContainer Container
        {
            get
            {
                if (!HttpContext.Current.Items.Contains(HttpContextKey))
                {
                    HttpContext.Current.Items.Add(HttpContextKey, new CompositionContainer(catalog));
                }

                return (CompositionContainer)HttpContext.Current.Items[HttpContextKey];
            }
        }

        private object Resolve(Type serviceType, string key = null)
        {
            var exports = this.Container.GetExports(serviceType, null, null);
            if (exports.Any())
            {
                return exports.First().Value;
            }

            var instance = defaultLocator.GetInstance(serviceType, key);
            if (instance != null)
            {
                return instance;
            }

            throw new ActivationException(string.Format("Could not resolve service type {0}.", serviceType.FullName));
        }

        private IEnumerable<object> ResolveAll(Type serviceType)
        {
            var exports = this.Container.GetExports(serviceType, null, null);
            if (exports.Any())
            {
                return exports.Select(e => e.Value).AsEnumerable();
            }

            var instances = defaultLocator.GetAllInstances(serviceType);
            if (instances != null)
            {
                return instances;
            }

            throw new ActivationException(string.Format("Could not resolve service type {0}.", serviceType.FullName));
        }

        #region IMvcServiceLocator Members

        public void Release(object instance)
        {
            var export = instance as Lazy<object>;
            if (export != null)
            {
                this.Container.ReleaseExport(export);
            }

            defaultLocator.Release(export);
        }

        #endregion

        #region IServiceLocator Members

        public IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return ResolveAll(serviceType);
        }

        public IEnumerable<TService> GetAllInstances<TService>()
        {
            var instances = ResolveAll(typeof(TService));
            foreach (TService instance in instances)
            {
                yield return (TService)instance;
            }
        }

        public TService GetInstance<TService>(string key)
        {
            return (TService)Resolve(typeof(TService), key);
        }

        public object GetInstance(Type serviceType)
        {
            return Resolve(serviceType);
        }

        public object GetInstance(Type serviceType, string key)
        {
            return Resolve(serviceType, key);
        }

        public TService GetInstance<TService>()
        {
            return (TService)Resolve(typeof(TService));
        }

        #endregion

        #region IServiceProvider Members

        public object GetService(Type serviceType)
        {
            return Resolve(serviceType);
        }

        #endregion
    }

    HOLY SCHMOLEY! That is a lot of code. Let’s break it down…

    First of all, I have 3 constructors. 2 for convenience, one for MEF. Since the MefServiceLocator will be instantiated in Global.asax and I only want one instance of it to live in the application, I have to do a dirty trick: whenever MEF wants to create a new MefServiceLocator for some reason (should in theory only happen once per request, but I want this thing to live application-wide), I’m giving it indeed a new instance which at least shares the part catalog with the one I originally created. Don’t shoot me for doing this…

    Next, you will also notice that I’m using a “fallback” locator, which in theory will be the instance stored in MvcServiceLocator.Default, which is ASP.NET MVC 3’s default MvcServiceLocator. I’m doing this for a reason though… read my disclaimer again: I stated that everything should be decorated with the [Export] attribute when I’m relying on MEF. Now since the services exposed by ASP.NET MVC 3, like the IFilterProvider, are not decorated with this attribute, MEF will not be able to find those. When I find myself in that situation, the MefServiceLocator is simply asking the default service locator for it. Not a beauty, but it works and makes my life easy.

    Wiring things

    To wire this thing, all it takes is adding 3 lines of code to my Global.asax. For clarity, I’m giving you my entire Global.asax Application_Start method:

    protected void Application_Start()
    {
        // Register areas

        AreaRegistration.RegisterAllAreas();

        // Register filters and routes

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        // Register MEF catalogs

        var catalog = new DirectoryCatalog(
            Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
        MvcServiceLocator.SetCurrent(new MefServiceLocator(catalog, MvcServiceLocator.Default));
    }

    Can you spot the 3 lines of code? This is really all it takes to make the complete application use MEF where appropriate. (Ok, that is a bit of a lie since you would still have to implement a very small IFilterProvider if you want MEF in your action filters, but still.)

    Hooks

    The cool thing is: a lot of things are now requested in the service locator we just created. When browsing to my site index, here’s all the things that are requested:

    • Resolve called for serviceType: System.Web.Mvc.IControllerFactory
    • Resolve called for serviceType: Mvc3WithMEF.Controllers.HomeController
    • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
    • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
    • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
    • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
    • Resolve called for serviceType: System.Web.Mvc.IViewEngine
    • Resolve called for serviceType: System.Web.Mvc.IViewEngine
    • Resolve called for serviceType: ASP.Index_cshtml
    • Resolve called for serviceType: System.Web.Mvc.IViewEngine
    • Resolve called for serviceType: System.Web.Mvc.IViewEngine
    • Resolve called for serviceType: ASP._LogOnPartial_cshtml

    Which means that you can now even inject stuff into views or compose their parts dynamically.

    Conclusion

    I have a strong sense of a power in here… ASP.NET MVC 3 will support DI natively if you want to use it, and I’ll be one of the users happily making use of it. There’s use cases for injecting/composing something in all of the above components, and ASP.NET MVC 3 made this just simpler and more straightforward.

    Here’s my sample code with some more examples in it: Mvc3WithMEF.zip (256.21 kb)


    ASP.NET MVC 3 preview 1 is out! Quick review...

    I just noticed a very interesting download: ASP.NET MVC 3 preview 1. Yes, you are reading this correctly, the first bits for v3.0 are there! Let’s have a quick look around and see what’s new...

    kick it on DotNetKicks.com

    Razor Syntax View Engine

    ScottGu blogged about Razor before. ASP.NET MVC has always supported the concept of “view engines”, pluggable modules that allow you to have your views rendered by different engines like for example the WebForms engine, Spark, NHAML, …

    Razor is a new view engine, focused on less code clutter and shorter code-expressions for generating HTML dynamically. As an example, have a look at the following view:

    <ul>
      <% foreach (var c in Model.Customers) { %>
        <li><%:c.DisplayName%></li>
      <% } %>
    </ul>

    In Razor syntax, this becomes:

    <ul>
      @foreach (var c in Model.Customers) {
        <li>@c.DisplayName</li>
      }
    </ul>

    Perhaps not the best example to show the strengths of this new engine, but do bear in mind that Razor simply puts code literally in your HTML, making it develop faster (did I mention perfect IntelliSense support in the Razor view editor?).

    Also, there’s a nice addition to the “Add View” dialog in Visual Studio: you can now choose for which view engine you want to generate a view.

    Razor view engine - Add view dialog

    ViewData dictionary “dynamic” support

    .NET 4 introduced the “dynamic” keyword, which is abstracting away a lot of reflection code you’d normally have to write yourself. The fun thing is, that the MVC guys abused this thing in a very nice way.

    Controller action method, ASP.NET MVC 2:

    public ActionResult Index()
    {
        ViewModel["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }

    Controller action method, ASP.NET MVC 3:

    public ActionResult Index()
    {
        ViewModel.Message = "Welcome to ASP.NET MVC!";

        return View();
    }

    “Isn’t that the same?” – Yes, in essence it is exactly the same concept at work. However, by using the dynamic keyword, there’s less “string pollution” in my code. Do note that in most situations, you would create a custom “View Model” and pass that to the view instead of using this ugly dictionary or dynamic object. Nevertheless: I do prefer reading code that uses less dictionaries.

    So far for the controller side, there’s also the view side. Have a look at this:

    @inherits System.Web.Mvc.WebViewPage

    @{
        View.Title = "Home Page";
        LayoutPage = "~/Views/Shared/_Layout.cshtml";
    }

    <h2>@View.Message</h2>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
    </p>

    A lot of new stuff there, right? First of all the Razor syntax, but secondly… There’s just something like @View.Message in this view, and this is rendering something from the ViewData dictionary/dynamic object. Again: very readable and understandable.

    It’s a small change on the surface, but I do like it. In my opinion, it’s more readable than using the ViewData dictionary when you are not using a custom view model.

    Global action filters

    Imagine you have a team of developers, all writing controllers. Imagine that they have to add the [HandleError] action filter to every controller, and they sometimes tend to forget… That’s where global action filters come to the rescue! Add this line to Global.asax:

    GlobalFilters.Filters.Add(new HandleErrorAttribute());

    This will automatically register that action filter attribute for every controller and action method.

    Fun fact: I blogged about exactly this feature about a year ago: Application-wide action filters in ASP.NET MVC. Want it in ASP.NET MVC 2? Go and get it :-)

    Cool, eh? Well… I do have mixed feelings about this… I can imagine there are situations where you want to do this more selectively. Here’s my call-out to the ASP.NET MVC team:

    • At least allow to specify action filters on a per-area level, so I can have my “Administration” area have other filters than my default area.
    • In an ideal world, I’d prefer something where I can specify global action filters even more granularly. This can be done using some customizing, but it would be useful to have it out-of-the-box. Here's an example of the ideal world:

    GlobalFilters.Filters.AddTo<HomeController>(new HandleErrorAttribute()) 
                         .AddTo<AccountController>(c => c.ChangePassword(), new AuthorizeAttribute());

    Dependency injection support

    I’m going to be short on this one: there’s 4 new hooks for injecting dependencies:

    • When creating controller factories
    • When creating controllers
    • When creating views (might be interesting!)
    • When using action filters

    More on that in my next post on ASP.NET MVC 3, as I think it deserves a full post rather than jut some smaller paragraphs.

    Update: Here's that next post on ASP.NET MVC 3 and dependency injection / MEF

    New action result types

    Not very ground-skaing news, but there's a new set of ActionResult variants available which will make your life easier:

    • HttpNotFoundResult - Speaks for itself, right :-)
    • HttpStatusCodeResult - How about new HttpStatusCodeResult(418, "I'm a teapot");
    • RedirectPermanent, RedirectToRoutePermanent, RedirectToActionPermanent - Writes a permanent redirect header

    Conclusion

    I only touched the tip of the iceberg. There’s more to ASP.NET MVC 3 preview 1, described in the release notes.

    In short, I’m very positive about the amount of progress being made in this framework! Very pleased with the DI portion of it, on which I’ll do a blog post later.

    Update: Here's that next post on ASP.NET MVC 3 and dependency injection / MEF


    Categories: ASP.NET | C# | General | MVC

    Renewed MVP ASP.NET for 2010!

    Just got the best e-mail a Microsoft community member can receive in his mailbox:MVPLogo

    Dear Maarten Balliauw,

    Congratulations! We are pleased to present you with the 2010 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others. We appreciate your outstanding contributions in ASP/ASP.NET technical communities during the past year.

    (...)

    Toby Richards
    General Manager
    Community Support Services

    I wish to thank everyone who has been supporting me, encouraging me, challenging me and thus bringing me to a second year of MVP duty. I will try to achieve the same for next year: do a lot of sessions, work on open-source, do blog posts, …

    With that: if you are not yet at lest challenging me, please do start doing this. It only helps me to learn from your problems which will in turn help the whole community to learn from it.

    I can almost copy my blog post from last year on the next part: I will leave the community for the next weeks to enjoy a nice vacation in Austria. No, not traveling for work this time. My next two weeks community will be a community of mountains, sun, große Bier and rest (not REST!). See you!


    Categories: ASP.NET | C# | General | MVP | Personal

    ASP.NET MVC - MvcSiteMapProvider 2.0 is out!

    I’m very proud to announce the release of the ASP.NET MVC MvcSiteMapProvider 2.0! I’m also proud that the name of this product now exceeds the average length of Microsoft product names. In this blog post, I will give you a feel of what you can (and can not) do with this ASP.NET-specific SiteMapProvider.

    As a warning: if you’ve used version 1 of this library, you will notice that I have not thought of backwards compatibility. A lot of principles have also changed. For good reasons though: this release is a rewrite of the original version with improved features, extensibility and stability.

    The example code is all based on the excellent ASP.NET MVC Music Store sample application by Jon Galloway.

    Getting the bits

    As always, the bits are available on CodePlex: MvcSiteMapProvider 2.0.0
    If you prefer to have the full source code, download the example application or check the source code tab on CodePlex.

    Introduction

    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.

    Based on areas, controller and action method names rather than hardcoded URL references, sitemap nodes are completely dynamic based on the routing engine used in an application. The dynamic character of ASP.NET MVC is followed in the MvcSiteMapProvider: there are numerous extensibility points that allow you to extend the basic functionality offered.

    Registering the provider

    After downloading the MvcSiteMapProvider, you will have to add a reference to the assembly in your project. Also, you will have to register the provider in your Web.config file. Add the following code somewhere in the <system.web> section:

    <siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
      <providers>
        <clear />
        <add name="MvcSiteMapProvider"
             type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
             siteMapFile="~/Mvc.Sitemap"
             securityTrimmingEnabled="true"
             enableLocalization="true"
             scanAssembliesForSiteMapNodes="true"
             skipAssemblyScanOn=""
             attributesToIgnore="bling"
             nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider"
             controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider"
             actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider"
             aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider"
             />
      </providers>
    </siteMap>

    The following configuration directives can be specified:

    Directive Required? Default Description
    siteMapFile No ~/Web.sitemap The sitemap XML file to use.
    securityTrimmingEnabled No false Use security trimming? When enabled, nodes that the user can not access will not be displayed in any sitemap control.
    enableLocalization No false Enables localization of sitemap nodes.
    scanAssembliesForSiteMapNodes No false Scan assemblies for sitemap nodes defined in code?
    skipAssemblyScanOn No (empty) Comma-separated list of assemblies that should be skipped when scanAssembliesForSiteMapNodes is enabled.
    attributesToIgnore No (empty) Comma-separated list of attributes defined on a sitemap node that should be ignored by the MvcSiteMapProvider.
    nodeKeyGenerator No MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider Class that will be used to generate sitemap node keys.
    controllerTypeResolver No MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider Class that will be used to resolve the controller for a specific sitemap node.
    actionMethodParameterResolver No MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider Class that will be used to determine the list of parameters on a sitemap node.
    aclModule No MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider Class that will be used to verify security and access rules for sitemap nodes.

     

    Creating a first sitemap

    The following is a simple sitemap XML file that can be used with the MvcSiteMapProvider:

    <?xml version="1.0" encoding="utf-8" ?>
    <mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0" enableLocalization="true">
      <mvcSiteMapNode title="Home" controller="Home" action="Index" changeFrequency="Always" updatePriority="Normal">
        <mvcSiteMapNode title="Browse Store" controller="Store" action="Index" />
        <mvcSiteMapNode title="Checkout" controller="Checkout" />
      </mvcSiteMapNode>
    </mvcSiteMap>

    The following attributes can be given on an XML node element:

    Attribute Required? Default Description
    title Yes (empty) The title of the node.
    description No (empty) Description of the node.
    area No (empty) The MVC area for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
    controller Yes (empty) The MVC controller for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
    action Yes (empty) The MVC action method for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
    key No (autogenerated) The unique identifier for the node.
    url No (autogenerated based on routes) The URL represented by the node.
    roles No (empty) Comma-separated list of roles allowed to access the node and its child nodes.
    resourceKey No (empty) Optional resource key.
    clickable No True Is the node clickable or just a grouping node?
    targetFrame No (empty) Optional target frame for the node link.
    imageUrl No (empty) Optional image to be shown by supported HtmlHelpers.
    lastModifiedDate No (empty) Last modified date for the node.
    changeFrequency No Undefined Change frequency for the node.
    updatePriority No Undefined Update priority for the node.
    dynamicNodeProvider No (empty) A class name implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider and providing dynamic nodes for the site map.

     

    Defining sitemap nodes in code

    In some cases, defining a sitemap node in code is more convenient than defining it in a sitemap xml file. To do this, decorate an action method with the MvcSiteMapNodeAttribute attribute. For example:

    // GET: /Checkout/Complete
    [MvcSiteMapNodeAttribute(Title = "Checkout complete", ParentKey = "Checkout")]
    public ActionResult Complete(int id)
    {
        // ...
    }

    Note that the ParentKey property should be specified to ensure the MvcSiteMapProvider  can determine the hierarchy for all nodes.

    Dynamic sitemaps

    In many web applications, sitemap nodes are directly related to content in a persistent store like a database.For example, in an e-commerce application, a list of product details pages in the sitemap maps directly to the list of products in the database. Using dynamic sitemaps, a small class can be provided to the MvcSiteMapProvider offering a list of dynamic nodes that should be incldued in the sitemap. This ensures the product pages do not have to be specified by hand in the sitemap XML.

    First of all, a sitemap node should be defined in XML. This node will serve as a template and tell the MvcSiteMapProvider infrastructure to use a custom dynamic node procider:

    <mvcSiteMapNode title="Details" action="Details" dynamicNodeProvider="MvcMusicStore.Code.StoreDetailsDynamicNodeProvider, MvcMusicStore" />

    Next, a class implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider or extending MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase should be created in your application code. Here’s an example:

    public class StoreDetailsDynamicNodeProvider
        : DynamicNodeProviderBase
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();

        public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            // Build value
            var returnValue = new List<DynamicNode>();

            // Create a node for each album
            foreach (var album in storeDB.Albums.Include("Genre"))
            {
                DynamicNode node = new DynamicNode();
                node.Title = album.Title;
                node.ParentKey = "Genre_" + album.Genre.Name;
                node.RouteValues.Add("id", album.AlbumId);

                returnValue.Add(node);
            }

            // Return
            return returnValue;
        }
    }

    Cache dependency

    When providing dynamic sitemap nodes to the MvcSiteMapProvider, chances are that the hierarchy of nodes will become stale, for example when adding products in an e-commerce website. This can be solved by specifying a CacheDescriptor on your MvcSiteMapProvider.Extensibility.IDynamicNodeProvider implementation:

    public class StoreDetailsDynamicNodeProvider
        : DynamicNodeProviderBase
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();

        public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            // ...
        }

        public override CacheDescription GetCacheDescription()
        {
            return new CacheDescription("StoreDetailsDynamicNodeProvider")
            {
                SlidingExpiration = TimeSpan.FromMinutes(1)
            };
        }
    }

    HtmlHelper functions

    The MvcSiteMapProvider provides different HtmlHelper extension methods which you can use to generate SiteMap-specific HTML code on your ASP.NET MVC views. Here's a list of available HtmlHelper extension methods.

    • Html.MvcSiteMap().Menu() - Can be used to generate a menu
    • Html.MvcSiteMap().SubMenu() - Can be used to generate a submenu
    • Html.MvcSiteMap().SiteMap() - Can be used to generate a list of all pages in your sitemap
    • Html.MvcSiteMap().SiteMapPath() - Can be used to generate a so-called "breadcrumb trail"
    • Html.MvcSiteMap().SiteMapTitle() - Can be used to render the current SiteMap node's title

    Note that these should be registered in Web.config, i.e. under <pages> add the following:

    <pages>
        <controls>
            <! -- ... -->
        </controls>
        <namespaces>
            <! -- ... -->
            <add namespace="MvcSiteMapProvider.Web.Html" />
        </namespaces>
    </pages>

    Action Filter Attributes

    SiteMapTitle

    In some situations, you may want to dynamically change the SiteMap.CurrentNode.Title in an action method. This can be done manually by setting SiteMap.CurrentNode.Title, or by adding the SiteMapTitle action filter attribute.

    Imagine you are building a blog and want to use the Blog's Headline property as the site map node title. You can use the following snippet:

    [SiteMapTitle("Headline")]
    public ViewResult Show(int blogId) {
       var blog = _repository.Find(blogIdId);
       return blog;
    }

    You can also use a non-strong typed ViewData value as the site map node title:

    [SiteMapTitle("SomeKey")]
    public ViewResult Show(int blogId) {
       ViewData["SomeKey"] = "This will be the title";

       var blog = _repository.Find(blogIdId);
       return blog;
    }

    Exporting the sitemap for search engine indexing

    When building a website, chances are that you want to provide an XML sitemap used for search engine indexing. The XmlSiteMapResult class creates an XML sitemap that can be submitted to Google, Yahoo and other search engines to help them crawl your website better. The usage is very straightforward:

    public class HomeController
    {
        public ActionResult SiteMapXml()
        {
            return new XmlSiteMapResult();
        }
    }

    Optionally, a starting node can also be specified in the constructor of theXmlSiteMapResult .

    Conclusion

    Get it while it’s hot! MvcSiteMapProvider 2.0.0 is available on CodePlex.

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | General | MVC | Projects | Software | XML

    Running on Windows Azure - ChronoRace - Autoscaling

    image At RealDolmen, we had the luck of doing the first (known) project on Windows Azure in Belgium. Together with Microsoft, we had the opportunity to make the ChronoRace website robust enough to withstand large sports events like the 20km through Brussels.

    ChronoRace is a Belgian company based in Malmédy, specialised in electronic timing for large sports events (around 340 per year) troughout Europe in different disciplines like jogging, cycling, sailing, … Each participant is registered through the website, can consult their results and can view a high-definition video of their arrival at the finish line. Of course, these results and videos are also used to brag to co-workers and family members, resulting in about 10 result and video views per participant. Imagine 20.000 or more participants on large events… No wonder their current 1 webserver – 1 database server setup could not handle all load.

    Extra investments in hardware, WAN connection upgrades and video streaming were considered, however found too expensive to have these running for 365 days a year while on average this extra capacity would only be needed for 14 days a year. Ideal for cloud computing! Especially with an expected 30.000 participants for the 20km through Brussels... (which would mean 3 TB of data transfers for the videos on 1 day!!!)

    Microsoft selected RealDolmen as a partner for this project, mainly because of the knowledge we built over the past year, since the first Azure CTP. Together with ChronoRace, we gradually moved the existing SQL Server databse to SQL Azure. After that, we started moving the video streaming to blob storage, implemented extra caching and automatic scaling.

    You probably have seen the following slides in various presentations on cloud computing:

    Capacity cloud computing

    All marketing gibberish, right? Nope! We actually managed to get very close to this model using our custom autoscaling mechanism. Here are some figures we collected during the peak of the 20km through Brussels:

    Windows Azure Auto Scaling

    More information on the technical challenges we encountered can be found in my slide deck I presented at KAHOSL last week:

    If you want more information on scalability and automatic scaling, feel free to come to the Belgian Community Day 2010 where I will be presenting a session on this topic.

    Oh and for the record: I’m not planning on writing marketing posts. I just was so impressed by our actual autoscaling graph that I had to blog this :-)


    Taking Care of a Cloud Environment (slides)

    It looks like I’m only doing sessions lately :-) Here’s another slide deck for a presentation I did on the Architect Forum last week in Belgium.

    Abstract: “No, this session is not about greener IT. Learn about using the RoleEnvironment and diagnostics provided by Windows Azure. Communication between roles, logging and automatic upscaling of your application are just some of the possibilities of what you can do if you know about how the Windows Azure environment works.”

    Thanks for attending!


    Slides of our VISUG session

    As promised, here are the slides of the VISUG session me and Kris van der Mast did yesterday.

    Being a pimp without Silverlight!

    Abstract: “Don't tell us you're jealous of those Silverlight fanboys! We'll show you that applications with bling can be developed using ASP.NET MVC and jQuery. We're talking MVC, template helpers, AJAX, JSON, transitions, live bindings, ...”