Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

NAVIGATION - SEARCH

Translating routes (ASP.NET MVC and Webforms)

Localized route in ASP.NET MVC - Translated route in ASP.NET MVC For one of the first blog posts of the new year, I thought about doing something cool. And being someone working with ASP.NET MVC, I thought about a cool thing related to that: let’s do something with routes! Since System.Web.Routing is not limited to ASP.NET MVC, this post will also play nice with ASP.NET Webforms. But what’s the cool thing? How about… translating route values?

Allow me to explain… I’m tired of seeing URLs like http://www.example.com/en/products and http://www.example.com/nl/products. Or something similar, with query parameters like “?culture=en-US”. Or even worse stuff. Wouldn’t it be nice to have http://www.example.com/products mapping to the English version of the site and http://www.exaple.com/producten mapping to the Dutch version? Better to remember when giving away a link to someone, better for SEO as well.

Of course, we do want both URLs above to map to the ProductsController in our ASP.NET MVC application. We do not want to duplicate logic because of a language change, right? And what’s more: it’s not fun if this would mean having to switch from <%=Html.ActionLink(…)%> to something else because of this.

Let’s see if we can leverage the routing engine in System.Web.Routing for this…

Want the sample code? Check LocalizedRouteExample.zip (23.25 kb)

Mapping a translated route

First things first: here’s how I see a translated route being mapped in Global.asax.cs:

[code:c#]

routes.MapTranslatedRoute(
    "TranslatedRoute",
    "{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = "" },
    new { controller = translationProvider, action = translationProvider },
    true
);

[/code]

Looks pretty much the same as you would normally map a route, right? There’s only one difference: the new { controller = translationProvider, action = translationProvider } line of code. This line of code basically tells the routing engine to use the object translationProvider as a provider which allows to translate a route value. In this case, the same translation provider will handle translating controller names and action names.

Translation providers

The translation provider being used can actually be anything, as long as it conforms to the following contract:

[code:c#]

public interface IRouteValueTranslationProvider
{
    RouteValueTranslation TranslateToRouteValue(string translatedValue, CultureInfo culture);
    RouteValueTranslation TranslateToTranslatedValue(string routeValue, CultureInfo culture);
}

[/code]

This contract provides 2 method definitions: one for mapping a translated value to a route value (like: mapping the Dutch “Thuis” to “Home”). The other method will do the opposite.

TranslatedRoute

The “core” of this solution is the TranslatedRoute class. It’s basically an overridden implementation of the System.Web.Routing.Route class, using the IRouteValueTranslationProvider for translating a route. As a bonus, it also tries to set the current thread culture to the CultureInfo detected based on the route being called. Note that this is just a reasonable guess, not the very truth. It will not detect nl-NL versus nl-BE, for example. Here’s the code:

[code:c#]

public class TranslatedRoute : Route
{
    // ...

    public RouteValueDictionary RouteValueTranslationProviders { get; private set; }

    // ...

    public override RouteData GetRouteData(HttpContextBase httpContext)
    { 
        RouteData routeData = base.GetRouteData(httpContext);
        if (routeData == null) return null;

        // Translate route values
        foreach (KeyValuePair<string, object> pair in this.RouteValueTranslationProviders)
        {
            IRouteValueTranslationProvider translationProvider = pair.Value as IRouteValueTranslationProvider;
            if (translationProvider != null
                && routeData.Values.ContainsKey(pair.Key))
            {
                RouteValueTranslation translation = translationProvider.TranslateToRouteValue(
                    routeData.Values[pair.Key].ToString(),
                    CultureInfo.CurrentCulture);

                routeData.Values[pair.Key] = translation.RouteValue;

                // Store detected culture
                if (routeData.DataTokens[DetectedCultureKey] == null)
                {
                    routeData.DataTokens.Add(DetectedCultureKey, translation.Culture);
                }

                // Set detected culture
                if (this.SetDetectedCulture)
                {
                    System.Threading.Thread.CurrentThread.CurrentCulture = translation.Culture;
                    System.Threading.Thread.CurrentThread.CurrentUICulture = translation.Culture;
                }
            }
        }

        return routeData;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        RouteValueDictionary translatedValues = values;

        // Translate route values
        foreach (KeyValuePair<string, object> pair in this.RouteValueTranslationProviders)
        {
            IRouteValueTranslationProvider translationProvider = pair.Value as IRouteValueTranslationProvider;
            if (translationProvider != null
                && translatedValues.ContainsKey(pair.Key))
            {
                RouteValueTranslation translation =
                    translationProvider.TranslateToTranslatedValue(
                        translatedValues[pair.Key].ToString(), CultureInfo.CurrentCulture);

                translatedValues[pair.Key] = translation.TranslatedValue;
            }
        }

        return base.GetVirtualPath(requestContext, translatedValues);
    }
}

[/code]

The GetRouteData finds a corresponding route translation if I entered “/Thuis/Over” in the URL. The GetVirtualPath method does the opposite, and will be used for mapping a call to <%=Html.ActionLink(“About”, “About”, “Home”)%> to a route like “/Thuis/Over” if the current thread culture is nl-NL. This is not rocket science, it simply tries to translate every token in the requested path and update the route data with it so the ASP.NET MVC subsystem will know that “Thuis” maps to HomeController.

Tying everything together

We already tied the route definition in Global.asax.cs earlier in this blog post, but let’s do it again with a sample DictionaryRouteValueTranslationProvider that will be used for translating routes. This one goes in Global.asax.cs:

[code:c#]

public static void RegisterRoutes(RouteCollection routes)
{
    CultureInfo cultureEN = CultureInfo.GetCultureInfo("en-US");
    CultureInfo cultureNL = CultureInfo.GetCultureInfo("nl-NL");
    CultureInfo cultureFR = CultureInfo.GetCultureInfo("fr-FR");

    DictionaryRouteValueTranslationProvider translationProvider = new DictionaryRouteValueTranslationProvider(
        new List<RouteValueTranslation> {
            new RouteValueTranslation(cultureEN, "Home", "Home"),
            new RouteValueTranslation(cultureEN, "About", "About"),
            new RouteValueTranslation(cultureNL, "Home", "Thuis"),
            new RouteValueTranslation(cultureNL, "About", "Over"),
            new RouteValueTranslation(cultureFR, "Home", "Demarrer"),
            new RouteValueTranslation(cultureFR, "About", "Infos")
        }
    );

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapTranslatedRoute(
        "TranslatedRoute",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" },
        new { controller = translationProvider, action = translationProvider },
        true
    );

    routes.MapRoute(
        "Default",      // Route name
        "{controller}/{action}/{id}",   // URL with parameters
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    );

}

[/code]

This is basically it! What I can now do is set the current thread’s culture to, let’s say fr-FR, and all action links generated by ASP.NET MVC will be using French. Easy? Yes! Cool? Yes!

Localizing ASP.NET MVC routing

Want the sample code? Check LocalizedRouteExample.zip (23.25 kb)

PHPMEF 0.1.0 released!

PHP MEF A while ago, I did a conceptual blog post on PHP Managed Extensibility Framework – PHPMEF. Today, I’m proud to announce the first public release of PHPMEF! After PHPExcel, PHPLinq, PHPPowerPoint and the Windows Azure SDK for PHP, PHPMEF is the 5th open-source project I started on interoperability (or conceptual interoperability) between the Microsoft world and the PHP world. Noble price for peace upcoming :-)

What is this thing?

PHPMEF is a PHP port of the .NET Managed Extensibility Framework, allowing easy composition and extensibility in an application using the Inversion of Control principle and 2 easy keywords: @export and @import.

PHPMEF is based on a .NET library, MEF, targeting extensibility of projects. It allows you to declaratively extend your application instead of requiring you to do a lot of plumbing. All this is done with three concepts in mind: export, import and compose. “PHPMEF” uses the same concepts in order to provide this extensibility features.

Show me an example!

Ok, I will. But not here. Head over to http://phpmef.codeplex.com and have a look at the principles and features behind PHPMEF.

Enjoy!

Creating an external facing Azure Worker Role endpoint

Internet facing Azure Worker Role When Windows Azure was first released, only Web Roles were able to have an externally facing endpoint. Since PDC 2009, Worker Roles can now also have an external facing endpoint, allowing for a custom application server to be hosted in a Worker Role. Another option would be to run your own WCF service and have it hosted in a Worker Role. Features like load balancing, multiple instances of the Worker are all available. Let’s see how you can create a simple TCP service that can display the current date and time.

Here’s what I want to see when I connect to my Azure Worker Role using telnet (“telnet efwr.cloudapp.net 1234”):

Telnet Azure Worker Role

Let’s go ahead and build this thing. Example code can be downloaded here: EchoCloud.zip (9.92 kb)

kick it on DotNetKicks.com

Configuring the external endpoint

Fire up your Visual Studio and create a new Cloud Service, named EchoCloud, with one Worker Role (named EchoWorker). After you complete this, you should have a Windows Azure solution containing one Worker Role. Right-click the worker role and select Properties. Browse to the Endpoints tab and add a new endpoint, like so:

Configuring an external endpoint on a Windows Azure Worker Role

This new endpoint (named EchoEndpoint) listens on an external TCP port with port number 1234. Note that you can also make this an internal endpoint, which is an endpoint that can only be reached within your Windows Azure solution and not from an external PC. This can be useful if you wan to host a custom application server in your project and make it available for other Web and Worker Roles in your solution.

Building the worker role

As you know, a Worker Role (in the WorkerRole.cs file in your newly created solution) consists of 3 methods that can be implemented: OnStart, Run and OnStop. There’s also an event handler RoleEnvironmentChanging available. The method names sort of speak for themselves, but allow me to explain quickly:

  • OnStart() is executed when the Worker Role is starting. Initializations and some checks can be done here.
  • Run() is the method which contains the actual Worker Role logic. The cool stuff goes in here :-)
  • OnStop() can be used to do things that should be done when the Worker Role is stopped.
  • RoleEnvironmentChanging() is the event handler that gets called when the environment changes: configuration changed, extra instances fired, … are possible triggers for this.

Our stuff will go in the Run() method. We’ll be creating a new TcpListener which will sit and accept connections. Whenever a connection is available, it will be dispatched on a second thread that will be communicating with the client. Let’s see how we can start the TcpListener:

[code:c#]

public class WorkerRole : RoleEntryPoint
{
    private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false);

    public override void Run()
    {
        TcpListener listener = null;
        try
        {
            listener = new TcpListener(
                RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["EchoEndpoint"].IPEndpoint);
            listener.ExclusiveAddressUse = false;
            listener.Start();
        }
        catch (SocketException)
        {
            Trace.Write("Echo server could not start.", "Error");
            return;
        }

        while (true)
        {
            IAsyncResult result = listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
            connectionWaitHandle.WaitOne();
        }
    }
}

[/code]

First thing to notice is that the TcpListener is initialized using the IPEndpoint from the current Worker Role instance:

[code:c#]

listener = new TcpListener(
                RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["EchoEndpoint"].IPEndpoint);

[/code]

We could have started the TcpListener using a static configuration telling it to listen on TCP port 1234, but that would be difficult for the Windows Azure platform. Instead, we start the TcpListener using the current IPEndpoint configuration that we set earlier in this blog post. This allows the application to run on the Windows Azure production environment, as well as on the development environment available from the Windows Azure SDK. Here’s how it would work if we had multiple Worker Roles hosting this application:

Multiple worker roles running a custom TCP server 

Second thing we are doing is starting the infinite loop that accepts connections and dispatches the connection to the HandleAsyncConnection method that will sit on another thread. This allows for having multiple connections into one Worker Role. Let’s have a look at the HandleAsyncConnection method:

[code:c#]

private void HandleAsyncConnection(IAsyncResult result)
{
    // Accept connection
    TcpListener listener = (TcpListener)result.AsyncState;
    TcpClient client = listener.EndAcceptTcpClient(result);
    connectionWaitHandle.Set();

    // Accepted connection
    Guid clientId = Guid.NewGuid();
    Trace.WriteLine("Accepted connection with ID " + clientId.ToString(), "Information");

    // Setup reader/writer
    NetworkStream netStream = client.GetStream();
    StreamReader reader = new StreamReader(netStream);
    StreamWriter writer = new StreamWriter(netStream);
    writer.AutoFlush = true;

    // Show application
    string input = string.Empty;
    while (input != "9")
    {
        // Show menu
        writer.WriteLine("…");

        input = reader.ReadLine();
        writer.WriteLine();

        // Do something
        if (input == "1")
        {
            writer.WriteLine("Current date: " + DateTime.Now.ToShortDateString());
        }
        else if (input == "2")
        {
            writer.WriteLine("Current time: " + DateTime.Now.ToShortTimeString());
        }

        writer.WriteLine();
    }

    // Done!
    client.Close();
}

[/code]

Code speaks for itself, no? One thing that you may find awkward is the connectionWaitHandle.Set();. In the previous code sample, we did connectionWaitHandle.WaitOne();. This means that we are not accepting any new connection until the current one is up and running. connectionWaitHandle.Set(); signals the original thread to start accepting new connections again.

Running the worker role

When running the application using the development fabric, you can fire up multiple instances. I fired up 4 Worker Roles that provide the simple TCP service that we just created. This means that my application will be load balanced, and every incoming connection will be distributed over these 4 Worker Role instances. Nifty!

Here’s a screenshot of my development fabric with two Worker Roles that I crashed intentionally. The service is still available, thanks to the fabric controller dispatching connections only to available Worker Role instances.

Development fabric with crashed worker roles

Example code

Example code can be downloaded here: EchoCloud.zip (9.92 kb)

Just a quick note: the approach described here can also be used to run a custom WCF host that has other bindings than for example basicHttpBinding.

kick it on DotNetKicks.com 

Ordering fields in ASP.NET MVC 2 templated helpers

Ever worked with the templated helpers provided by ASP.NET MVC 2? Templated helpers provide a way to automatically build UI based on a data model that is marked with attributes defined in the System.ComponentModel.DataAnnotations namespace. For example, a property in the model can be decorated with the attribute [DisplayFormat(DataFormatString = "{0:c}")], and the templated helpers will always render this field formatted as currency.

If you have worked with templated helpers, you must agree: they can be useful! There’s one thing which is impossible in the current version: ordering fields.

kick it on DotNetKicks.com

Take the following class and the rendered form using templated helpers:

ASP.NET MVC EditorForModel()

[code:c#]

public class Person
{
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

[/code]

Nice, but I would rather have the field “Email” displayed third. It would be nice if the field order could be applied using the same approach as with the System.ComponentModel.DataAnnotations namespace: let’s build us an attribute for it!

Building the OrderAttribute

Assuming you have already built an attribute once in your life, let’s go over this quickly:

[code:c#]

[global::System.AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class OrderAttribute : Attribute
{
    readonly int order;

    public OrderAttribute(int order)
    {
        this.order = order;
    }

    public int Order
    {
        get { return order; }
    }
}

[/code]

The OrderAttribute can be applied to any property of a model, and needs exactly one parameter: order. This order will be used to sort the fields being rendered. Here’s how our Person class may look like after applying the OrderAttribute:

[code:c#]

public class Person
{
    [Order(3)]
    public string Email { get; set; }

    [Order(1)]
    public string FirstName { get; set; }

    [Order(2)]
    public string LastName { get; set; }
}

[/code]

Speaks for itself, no? Now, before you stop reading: this will not work yet. The reason is that the default ModelMetadataProvider from the ASP.NET MVC framework, which provides the templated helpers all information they need about the model, does not know about this OrderAttribute. Let’s see what we can do about that…

Building the OrderedDataAnnotationsModelMetadataProvider

In order for the ASP.NET MVC framework to know and use the OrderAttribute created previously, we’re going to extend the default DataAnnotationsModelMetadataProvider provided with ASP.NET MVC 2. Here’s the code:

[code:c#]

public class OrderedDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    public override IEnumerable<ModelMetadata> GetMetadataForProperties(
        object container, Type containerType)
    {
        SortedDictionary<int, ModelMetadata> returnValue =
            new SortedDictionary<int, ModelMetadata>();

        int key = 20000; // sort order for "unordered" keys

        IEnumerable<ModelMetadata> metadataForProperties =
            base.GetMetadataForProperties(container, containerType);

        foreach (ModelMetadata metadataForProperty in metadataForProperties)
        {
            PropertyInfo property = metadataForProperty.ContainerType.GetProperty(
                metadataForProperty.PropertyName);

            object[] propertyAttributes = property.GetCustomAttributes(
                typeof(OrderAttribute), true);

            if (propertyAttributes.Length > 0)
            {
                OrderAttribute orderAttribute = propertyAttributes[0] as OrderAttribute;
                returnValue.Add(orderAttribute.Order, metadataForProperty);
            }
            else
            {
                returnValue.Add(key++, metadataForProperty);
            }
        }

        return returnValue.Values.AsEnumerable();
    }
}

[/code]

By overriding the GetMetadataForProperties, we’re hooking into the DataAnnotationsModelMetadataProvider’s moment of truth, the method where all properties of the model are returned as ModelMetadata. First of all, we’re using the ModelMetadata the base class provdes. Next, we use a little bit of reflection to get to the OrderAttribute (if specified) and use it to build a SortedDictionary of ModelMetadata. Easy!

One small caveat: non-decorated properties will always come last in the rendered output.

One thing left…

One thing left: registering the OrderedDataAnnotationsModelMetadataProvider with the ModelMetadataProviders infrastructure offered by ASP.NET MVC. Here’s how:

[code:c#]

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ModelMetadataProviders.Current = new OrderedDataAnnotationsModelMetadataProvider();
}

[/code]

I guess you know this one goes into Global.asax.cs. If all works according to plan, your rendered view should now look like the following, with the e-mail field third:

image

kick it on DotNetKicks.com

Vote to help me speak at the MIX 2010 conference!

Everybody knows the Microsoft MIX event, right? The one in Las Vegas? The one with all the fancy web-related stuff? Rings a bell? Ok, great. In the beginning of December 2009, Microsoft did an open call for speakers, which I answered with some session proposals. Who doesn’t want to go to Vegas, right?

The open call proposals have been processed (150+ sessions submitted, wow!) and a voting has started. Yes, you hear me coming: please go ahead and vote for a session I submitted. Voting ends January 15th, 2010.

Since I could not decide which color of the voting banner matched best with my blog’s theme, I decided to put them all three online:

image

Thanks in advance!

Maarten

PS: There's also Elijah Manor, Justin Etheredge, K. Scott Allen, and many others who submitted good looking sessions.

kick it on DotNetKicks.com

Byebye 2009, welcome 2010!

Fireworks!

A year just ended, a new has begun. Congratulations! Happy newyear 2010! And welcome in the next decennium. It’s time for a 2009 wrap-up post. And perhaps, even the time for some things I want to do in 2010. First things first: the top 5 blog posts of 2009.

Top 5 blog posts of 2009

1. More ASP.NET MVC Best Practices – A blog post gathering best practices from different sources and adds some more of my own.

2. Code based ASP.NET MVC GridView (2008: 4th) – This post gives a shot at a gridview for the ASP.NET MVC framework, built using expressions.

3. Code performance analysis in Visual Studio 2008 (2008: 2nd) – Visual Studio developer, did you know you have a great performance analysis (profiling) tool at your fingertips?

4. Building an ASP.NET MVC sitemap provider with security trimming (2008: 5th) – Building an ASP.NET MVC sitemap provider that can be used with standard ASP.NET sitemap controls.

5. ASP.NET load balancing and ASP.NET state server (aspnet_state) (2008: 3rd) – A how-to on load balancing with ASP.NET. Also related to ASP.NET Session State Partitioning and ASP.NET Session State Partitioning using State Server Load Balancing.

I did a similar list last year, and you can see that I actually can stop blogging. 4 posts are in the top 5 again, with one newcomer on the 1st place.

Open-source in 2009

What should I say about this one… I did a lot of that :-) I continued to work on PHPExcel, PHPLinq and PHPPowerPoint, started work (with Microsoft) on the Windows Azure SDK for PHP, released the ASP.NET MVC SiteMap Provider, released TwitterMatic on CodePlex and did some contributions to Zend Framework. Then there’s also my port of MEF to PHP, which I will be releasing somewhere in Q1 2010.

That’s a lot! And I really hope to keep the same pace going in 2010. I’m constructing a house though, which also takes some time… Luckily: no windows yet, so I can consider it open-source as well :-)

Presentations in 2009

Let’s see what we have there… A lot! And I hope to do the same amount (or more) in 2010. TechDays Belgium, TechDays Finland and PHPBenelux conference have been confirmed already, a good start. Anyway: here’s the 2009 list. Note that there’s also some more at various schools in Belgium, and internal sessions for RealDolmen.

Cloud computing and the Windows Azure Services Platform

This session covers the basics of the Windows Azure Services Platform and drills into some architectural challenges. Learn what components the Windows Azure Services Platform is built of and how they can be leveraged in building a scalable and reliable application.

KU Leuven, December 7, 2009

Slides: http://www.slideshare.net/maartenba/cloud-computing-and-the-windows-azure-services-platform-ku-leuven

MSDN - Converting an existing ASP.NET application to Windows Azure

Put your stuff in the cloud! Windows Azure allows you to take advantage of cloud computing infranstructure for hosting, computing, and storage of your applications. In this demo filled session we take an existing ASP.Net Application and move it to be hosted in Windows Azure, while taking advantage of Windows Azure storage.

MSDN Live Meeting for MSDN Belgium, November 24, 2009

Slides: http://www.slideshare.net/maartenba/msdn-converting-an-existing-aspnet-application-to-windows-azure
Demo code: MSDN - Converting an existing ASP.NET application to Windows Azure.zip (2.01 mb)
Live recording: http://www.microsoft.com/belux/MSDN/fr/chopsticks/default.aspx?id=1491

ASP.NET MVC Wisdom

Building a Twitter clone in 60 minutes, featuring what's new in ASP.NET MVC 2 preview 1 and focusing on some of the core ASP.NET MVC features like security and routing.

Remix Belgium 2009, Brussels, September 29, 2009

Slides: http://www.slideshare.net/maartenba/aspnet-mvc-wisdom
Demo code: ASP.NET MVC Wisdom - ReMix.zip (8.91 mb)
Live recording: http://blog.maartenballiauw.be/post/2009/10/23/Recording-of-my-session-at-Remix-2009-ASPNET-MVC.aspx

PHP and Silverlight

So you have an existing PHP application and would like to spice it up with a rich and attractive front-end. Next to Adobe Flex, you can also choose Silverlight as a solution. This session shows you around in Silverlight and shows that PHP and Silverlight can go together easily.

DevDays 2009, The Hague, May 29, 2009  -  May 29, 2009
Dutch PHP Conference, Amsterdam, June 12, 2009  -  June 12, 2009

Slides: http://www.slideshare.net/maartenba/php-and-silverlight-devdays-session?type=powerpoint
Demo code: PHP and Silverlight - DevDays.zip (1.00 mb)
Live recording (audio): http://techportal.ibuildings.com/2009/12/21/introduction-to-silverlight-for-php-developers/

PHPPowerPoint – More OpenXML from PHP

Session on the newly released open source project PHPPowerPoint, which enables you to build PPTX files from PHP. This session will provide you a general view of PHPPowerPoint and some architectural insights into the API.

DII Workshop, London, May 18, 2009  -  May 18, 2009

Mocking

This session provides an introduction to mocking and shows how test-driven development and the use of mocking frameworks such as Moq can speed up application development.

VISUG Belgium, May 7, 2009  -  May 7, 2009

Slides: http://www.slideshare.net/maartenba/mocking-visug-session?type=powerpoint
Demo code: MockingDemoCode.zip (1.64 mb)

ASP.NET MVC (model-view-controller)

This session provides an introduction to the ASP.NET MVC framework. Starting with a short intro to the model-view-controller pattern and Microsoft's vision on implementing this pattern for ASP.NET, over some of the key differences between ASP.NET Webforms and ASP.NET MVC to doing some more advanced things like scaffolding and action filters.

MSDN Belgium, April 23, 2009  -  April 23, 2009

Slides: http://www.slideshare.net/maartenba/msdn-aspnet-mvc?type=powerpoint

Azure User Group Belgium

image Together with Kurt Claeys and Yves Goeleven, we did a first session with the new Azure User Group Belgium. AZUG.BE is a Belgian user group with focus on development and architecture of the Windows Azure Platform. Our goal is to share knowledge and experiences with the .NET community on Windows Azure, .NET Services and SQL Azure.

Expect more activity and sessions in 2010!

I wrote a book!

image That’s correct: I did write a book. My first one, but probably not the last one. Don’t know when and on what, but I’ll probably find the energy to do this effort again. Here’s a quite from my announcement post:

It’s been quite a job, but there it is: Packt just announced my very first book on their site. It is titled “ASP.NET MVC 1.0 Quickly”, covering all aspects ASP.NET MVC offers in a to-the-point manner with hands-on examples. The book walks through the main concepts of the MVC framework to help existing ASP.NET developers to move on to a higher level. It includes clear instructions and lots of code examples. It takes a simple approach, thereby allowing you to work with all facets of web application development. Some keywords: Model-view-controller, ASP.NET MVC architecture and components, unit testing, mocking, AJAX using MS Ajax and jQuery, reference application and resources.

That’s it for the marketing part: let’s do a retrospective on the writing process itself. Oh and yes, those are my glasses on the cover. Photo was taken on the beach near Bray-Dunes (France).

Most valuable professional

image In July 2009, I became an MVP ASP.NET. Let’s see if we can repeat this for 2010 :-)

My focus was still on ASP.NET MVC in 2009, but also more and more on Windows Azure. These two make a great combination, and will be the main topics for my blog in 2010 as well.

Traveling

Some traveling in 2009 as well. I went on a ski vacation and a summer vacation, but next to that I did some traveling for work as well:

If only I knew that these last two would provide me a lot of airmiles… Subscribed at www.milesandmore.com for possible traveling in 2010 :-)

Thank you!

I do take credit for all that I did in 2009, but I would also like to thank my company RealDolmen for providing enough room to do some of this during work hours, as well as for challenging me in this. Really appreciated guys!

Now is also a great moment to thank my family for not whining too much and also giving me the room to do all of this, often taking time out of family life. Thanks for letting me do all this!

Summary and look ahead

It’s been a relaxing year. No, seriously: I’ve been busy, and I expect the same for 2010… Not sure if I can do 79 blog posts again, but I will sure try. And in order to do that, I need some input from you:

  • What topics would you like to see covered?
  • Is there anything you want to learn more about?
  • A great conceptual idea, no time to work on it? Shoot! Maybe I will work on it.
  • Any other feedback for me?

Enjoy 2010! I will.