Logo

Maarten Balliauw {blog}

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

About the author

Maarten Balliauw is an MVP ASP.NET and is currently employed as .NET Software Engineer at RealDolmen. His interests are mainly web applications developed in ASP.NET (C#) or PHP.
More about me More about me
Send mail E-mail me


Microsoft Most Valuable Professional - MVP - ASP.NET

Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn RealDolmen - Rock-solid passion for ICT
I'm a speaker at TechDays Belgium and TechDays Finland

Search

Latest Twitter

    Follow me on Twitter...

    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

    Sharpy - an ASP.NET MVC view engine based on Smarty

    Sharpy - ASP.NET MVC View Engine based on SmartyAre you also one of those ASP.NET MVC developers who prefer a different view engine than the default Webforms view engine available? You tried Spark, NHaml, …? If you are familiar with the PHP world as well, chances are you know Smarty, a great engine for creating views that can easily be read and understood by both developers and designers. And here’s the good news: Sharpy provides the same syntax for ASP.NET MVC!

    If you want more details on Sharpy, visit Jaco Pretorius’ blog:

    kick it on DotNetKicks.com

    A simple example…

    Here’s a simple example:

    {master file='~/Views/Shared/Master.sharpy' title='Hello World sample'}

    <h1>Blog entries</h1>

    {foreach from=$Model item="entry"}
        <tr>
            <td>{$entry.Name|escape}</td>       
            <td align="right">{$entry.Date|date_format:"dd/MMM/yyyy"}</td>       
        </tr>
        <tr>
            <td colspan="2" bgcolor="#dedede">{$entry.Body|escape}</td>
        </tr>
    {foreachelse}
        <tr>
            <td colspan="2">No records</td>
        </tr>
    {/foreach}

    The above example first specifies the master page to use. Next, a foreach-loop is executed for each blog post (aliased “entry”) in the $Model. Printing the entry’s body is done using {$entry.Body|escape}. Note the pipe “|” and the word escape after it. These are variable modifiers that can be used to escape content, format dates, …

    Extensibility

    Sharpy is all about extensibility: every function in a view is actually a plugin of a specific type (there are four types, IInlineFunction, IBlockFunction, IExpressionFunction and IVariableModifier). These plugins are all exposed through MEF. This means that Sharpy will always use any of your custom functions that are exposed through MEF. For example, here’s a custom function named “content”:

    [Export(typeof(IInlineFunction))]
    public class Content : IInlineFunction
    {
        public string Name
        {
            get { return "content"; }
        }

        public string Evaluate(IDictionary<string, object> attributes, IFunctionEvaluator evaluator)
        {
            // Fetch attributes
            var file = attributes.GetRequiredAttribute<string>("file");

            // Write output
            return evaluator.EvaluateUrl(file);
        }
    }

    Here’s how to use it:

    {content file='~/Content/SomeFile.txt'}

    Sharpy uses MEF to allow developers to implement their own functions and modifiers.  All the built-in functions are also built using this exact same framework – the same functionality is available to both internal and external functions.

    Extensibility is one of the strongest features in Sharpy.  This should allow us to leverage any functionality available in a normal ASP.NET view while maintaining simple views and straightforward markup.

    Give it a spin!

    Do give Sharpy a spin, you will learn to love it.


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

    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.23 kb).

    Mapping a translated route

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

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

    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:

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

    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:

    public class TranslatedRoute : Route
    {
        // ...

        public RouteValueDictionary RouteValueTranslationProviders { get; private set; }

        // ...

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

            // 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);
        }
    }

    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:

    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
        );

    }

    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.23 kb).

    kick it on DotNetKicks.com


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

    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()

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

    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:

    [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; }
        }
    }

    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:

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

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

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

    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:

    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();
        }
    }

    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:

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

        RegisterRoutes(RouteTable.Routes);

        ModelMetadataProviders.Current = new OrderedDataAnnotationsModelMetadataProvider();
    }

    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


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

    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


    Supporting multiple submit buttons on an ASP.NET MVC view

    Multiple buttons on an ASP.NET MVC view A while ago, I was asked for advice on how to support multiple submit buttons in an ASP.NET MVC application, preferably without using any JavaScript. The idea was that a form could contain more than one submit button issuing a form post to a different controller action.

    The above situation can be solved in many ways, one a bit cleaner than the other. For example, one could post the form back to one action method and determine which method should be called from that action method. Good solution, however: not standardized within a project and just not that maintainable… A better solution in this case was to create an ActionNameSelectorAttribute.

    Whenever you decorate an action method in a controller with the ActionNameSelectorAttribute (or a subclass), ASP.NET MVC will use this attribute to determine which action method to call. For example, one of the ASP.NET MVC ActionNameSelectorAttribute subclasses is the ActionNameAttribute. Guess what the action name for the following code snippet will be for ASP.NET MVC:

    public class HomeController : Controller
    {
        [ActionName("Index")]
        public ActionResult Abcdefghij()
        {
            return View();
        }
    }

    That’s correct: this action method will be called Index instead of Abcdefghij. What happens at runtime is that ASP.NET MVC checks the ActionNameAttribute and asks if it applies for a specific request. Now let’s see if we can use this behavior for our multiple submit button scenario.

    kick it on DotNetKicks.com

    The view

    Since our view should not be aware of the server-side plumbing, we can simply create a view that looks like this.

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MvcMultiButton.Models.Person>" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Create person</title>
        <script src="<%=Url.Content("~/Scripts/MicrosoftAjax.js")%>" type="text/javascript"></script>
        <script src="<%=Url.Content("~/Scripts/MicrosoftMvcAjax.js")%>" type="text/javascript"></script>
    </head>
    <body>

        <% Html.EnableClientValidation(); %>
        <% using (Html.BeginForm()) {%>

            <fieldset>
                <legend>Create person</legend>
                <p>
                    <%= Html.LabelFor(model => model.Name) %>
                    <%= Html.TextBoxFor(model => model.Name) %>
                    <%= Html.ValidationMessageFor(model => model.Name) %>
                </p>
                <p>
                    <%= Html.LabelFor(model => model.Email) %>
                    <%= Html.TextBoxFor(model => model.Email) %>
                    <%= Html.ValidationMessageFor(model => model.Email) %>
                </p>
                <p>
                    <input type="submit" value="Cancel" name="action" />
                    <input type="submit" value="Create" name="action" />
                </p>
            </fieldset>

        <% } %>

        <div>
            <%=Html.ActionLink("Back to List", "Index") %>
        </div>

    </body>
    </html>

    Note the two submit buttons (namely “Cancel” and “Create”), both named “action” but with a different value attribute.

    The controller

    Our controller should also not contain too much logic for determining the correct action method to be called. Here’s what I propose:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new Person());
        }

        [HttpPost]
        [MultiButton(MatchFormKey="action", MatchFormValue="Cancel")]
        public ActionResult Cancel()
        {
            return Content("Cancel clicked");
        }

        [HttpPost]
        [MultiButton(MatchFormKey = "action", MatchFormValue = "Create")]
        public ActionResult Create(Person person)
        {
            return Content("Create clicked");
        }
    }

    Some things to note:

    • There’s the Index action method which just renders the view described previously.
    • There’s a Cancel action method which will trigger when clicking the Cancel button.
    • There’s a Create action method which will trigger when clicking the Create button.

    Now how do these last two work… You may also have noticed the MultiButtonAttribute being applied. We’ll see the implementation in a minute. In short, this is a subclass for the ActionNameSelectorAttribute, triggering on the parameters MatchFormKey and MatchFormValues. Now let’s see how the MultiButtonAttribute class is built…

    The MultiButtonAttribute class

    Now do be surprised of the amount of code that is coming…

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class MultiButtonAttribute : ActionNameSelectorAttribute
    {
        public string MatchFormKey { get; set; }
        public string MatchFormValue { get; set; }

        public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
        {
            return controllerContext.HttpContext.Request[MatchFormKey] != null &&
                controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
        }
    }

    When applying the MultiButtonAttribute to an action method, ASP.NET MVC will come and call the IsValidName method. Next, we just check if the MatchFormKey value is one of the request keys, and the MatchFormValue matches the value in the request. Simple, straightforward and re-usable.

    kick it on DotNetKicks.com


    Microsoft PDC09 keynote highlights

    Finally found some time to write a short blog post on the announcements this morning at PDC 2009.Microsoft PDC keynote highlights Ray Ozzie started the keynote this morning, focusing on Microsoft’s “three-screen” vision for the future. There will be three screens connected to the cloud: TV, (handheld) devices and of course good old PC. This vision is driven by some key players: Windows 7, Internet Explorer, Silverlight and Windows Azure. Make sure to have a look at these four if you want to play in this future.

    Some announcements were made as well:

    Had a great day yesterday, driving trough the city of Los Angeles and looking at various places in town. Conference day one was also very interesting, lots of good sessions. Currently missing a session slot though, waiting for a Channel9 interview on the Windows Azure SDK for PHP. Stay tuned!


    Localize ASP.NET MVC 2 DataAnnotations validation messages

    Living in a country where there are there are three languages being used, almost every application you work on requires some form of localization. In an earlier blog post, I already mentioned ASP.NET MVC 2’s DataAnnotations support for doing model validation. Ever since, I was wondering if it would be possible to use resource files or something to do localization of error messages, since every example that could be found on the Internet looks something like this:

    [MetadataType(typeof(PersonBuddy))]
    public class Person
    {
        public string Name { get; set; }
        public string Email { get; set; }
    }

    public class PersonBuddy
    {
        [Required(ErrorMessage = "Name is required.")]
        public string Name { get; set; }

        [Required(ErrorMessage = "E-mail is required.")
        public string Email { get; set; }
    }

    Yes, those are hardcoded error messages. And yes, only in one language. Let’s see how localization of these would work.

    1. Create a resource file

    Add a resource file to your ASP.NET MVC 2 application. Not in App_GlobalResources or App_LocalResources, just a resource file in a regular namespace. Next, enter all error messages that should be localized in a key/value manner. Before you leave this file, make sure that the Access Modifier property is set to Public.

    Access modifier in resource file

    2. Update your “buddy classes”

    Update your “buddy classes” (or metadata classes or whatever you call them) to use the ErrorMessageResourceType and ErrorMessageResourceName parameters instead of the ErrorMessage parameter that you normally pass. Here’s the example from above:

    [MetadataType(typeof(PersonBuddy))]
    public class Person
    {
        public string Name { get; set; }
        public string Email { get; set; }
    }

    public class PersonBuddy
    {
        [Required(ErrorMessageResourceType = typeof(Resources.ModelValidation), ErrorMessageResourceName = "NameRequired")]
        public string Name { get; set; }

        [Required(ErrorMessageResourceType = typeof(Resources.ModelValidation), ErrorMessageResourceName = "EmailRequired")]
        public string Email { get; set; }
    }

    3. See it work!

    After creating a resource file and updating the buddy classes, you can go back to work and use model binders, ValidationMessage and ValidationSummary. ASP.NET will make sure that the correct language is used based on the thread culture info.

    Localized error messages

    kick it on DotNetKicks.com


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

    Recording of my session at Remix 2009 - ASP.NET MVC

    On September 29, I did a session on ASP.NET MVC at Remix 2009 Belgium. All session recordings are now online, check the Remix09 site. Slides and code for my talk can be found in a previous blog post. The video material can be found below. Enjoy! And feel free to leave some comments!

    kick it on DotNetKicks.com

    ASP.NET MVC Wisdom

    Abstract: "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."


    Let me Bing that for you

    Have you ever been bugged with stupid questions? Do you get tired of people asking stuff that is only one search engine query away? Chances are you answered both of these questions with “yes!”. Together with Phil Haack and Juliën Hanssens, I created LetMeBingThatForYou.com, a website that generates a search engine query for people who ask you questions they could easily answer by themselves.

    Yes, the idea is a copy of LetMeGoogleThatForYou.com. However, we thought Bing deserved something similar. We even got Bing’s picture of the day working. How cool is that?

    Find Chuck Norris

    One last note: this project is not associated with Microsoft nor Bing. We’re doing this project for fun.

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | Fun | General | ICT | Internet | MVC

    Leveraging ASP.NET MVC 2 futures “ViewState”

    Let’s start this blog post with a confession: yes, I abused a feature in the ASP.NET MVC 2 futures assembly to fire up discussion. In my previous blog post, I called something “ViewState in MVC” while it is not really ViewState. To be honest, I did this on purpose, wanting to see people discuss this possibly new feature in MVC 2. Discussion started quite fast: most people do not like the word ViewState, especially when it is linked to ASP.NET MVC. As Phil Haack pointed out in a comment on my previous blog post, I used this foul word where it was not appropriate.

    (…) I think calling it ViewState is very misleading. (…) what your serializing is the state of the Model, not the View. (…)

    That’s the truth! But… how should we call this then? There is already something called ModelState, and this is something different. Troughout this blog post, I will refer to this as “Serialized Model State”, or “SMS” in short. Not an official abbreviation, just something to have a shared meaning with you as a reader.

    So, SMS… Let’s use this in a practical example.

    kick it on DotNetKicks.com

    Example: Optimistic Concurrency

    Concurrency between old train and updated train. Every developer who has worked on a business application will definitely have come to deal with optimistic concurrency. Data retrieved from the database has a unique identifier and a timestamp, used for optimistic concurrency control. When editing this data, the identifier and timestamp have to be associated with the client operation, requiring a persistence mechanism. This mechanism should make sure the identifier and timestamp are preserved to verify if another user has updated it since it was originally retrieved.

    There are some options to do this: you can store this in TempData, in a Cookie or in Session state. A more obvious choice, however, would be a mechanism like “SMS”: it allows you to persist the model state on your view, allowing to retrieve the state of your model whenever data is posted to an action method. The fact that it is on your view, means that is linked to a specific request that will happen in the future.

    Let’s work with a simple Person class, consisting of Id, Name, Email and RowState properties. RowState will contain a DateTime value when the database record was last updated. An action method fetching data is created:

    [HttpGet]
    public ActionResult Edit(int id)
    {
        // Simulate fetching Person from database
        Person initialPersonFromDatabase = new Person
        {
            Id = id,
            FirstName = "Maarten",
            LastName = "Balliauw",
            Email = "",
            RowVersion = DateTime.Now
        };
        return View(initialPersonFromDatabase);
    }

    The view renders an edit form for our person:

    <h2>Concurrency demo</h2>

    <% Html.EnableClientValidation(); %>
    <%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>

    <% using (Html.BeginForm()) {%>
        <%=Html.Serialize("person", Model)%>

        <fieldset>
            <legend>Edit person</legend>
            <%=Html.EditorForModel()%>
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>

    <% } %>

    Let’s have a look at this view markup. <%=Html.EditorForModel()%> renders an editor for our model class Person. based on templates. This is a new feature in ASP.NET MVC 2.

    Another thing we do in our view is <%=Html.Serialize("person", Model)%>: this is a HtmlHelper extension persisting our model to a hidden form field:

    <input name="person" type="hidden" value="/wEymwIAAQAAAP
    ////8BAAAAAAAAAAwCAAAARE12YzJWaWV3U3RhdGUsIFZlcnNpb249MS4wL
    jAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBQEA
    AAAbTXZjMlZpZXdTdGF0ZS5Nb2RlbHMuUGVyc29uBAAAABo8Rmlyc3ROYW1
    lPmtfX0JhY2tpbmdGaWVsZBk8TGFzdE5hbWU+a19fQmFja2luZ0ZpZWxkFj
    xFbWFpbD5rX19CYWNraW5nRmllbGQbPFJvd1ZlcnNpb24+a19fQmFja2luZ
    0ZpZWxkAQEBAA0CAAAABgMAAAAHTWFhcnRlbgYEAAAACEJhbGxpYXV3BgUA
    AAAAqCw1nBkWzIgL"
    />

    Yes, this looks ugly and smells like ViewState, but it’s not. Let’s submit our form to the next action method:

    [HttpPost]
    public ActionResult Edit([Deserialize]Person person, FormCollection form)
    {
        // Update model
        if (!TryUpdateModel(person, form.ToValueProvider()))
            return View(person);

        // Simulate fetching person from database
        Person currentPersonFromDatabase = new Person
        {
            Id = person.Id,
            FirstName = "Maarten",
            LastName = "Balliauw",
            Email = "maarten@maartenballiauw.be",
            RowVersion = DateTime.Now
        };

        // Compare version with version from model state
        if (currentPersonFromDatabase.RowVersion > person.RowVersion)
        {
            // Concurrency issues!
            ModelState.AddModelError("Person", "Concurrency error: person was changed in database.");

            return View(person);
        }
        else
        {
            // Validation also succeeded
            return RedirectToAction("Success");
        }
    }

    Let’s see what happens here…The previous model state is deserialized from the hidden field we created in our view, and passed into the parameter person of this action method. Edited form values are in the FormCollection parameter. In the action method body, the deserialized model is updated first with the values from the FormCollection parameter. Next, the current database row is retrieved, having a newer RowVersion timestamp. This indicates that the record has been modified in the database and that we have a concurrency issue, rendering a validation message.

    Conclusion

    “ViewState” (or “SMS” or whatever it will be called) is really a useful addition to ASP.NET MVC 2, and I hope this blog post showed you one example usage scenario where it is handy. Next to that, you are not required to use this concept: it’s completely optional. So if you still do not like it, then do not use it. Go with Session state, Cookies, hidden fields, …

    kick it on DotNetKicks.com


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