MVCContrib extension – Localized Labels, Description

I use MVCContrib library on one of my ASP.NET MVC 2 projects. That project should have multi-language support. I use InputBuilders quite much but InputBuilders don’t support the multilanguage features for i.e. Labels, Descriptions, etc..

So I realized to extend it.

So to fully understand this blog entry you nee to be familiar with MvcContrib InputBuilders and the way how the default multi-language support is done in ASP.NET MVC.

There is quite nice article written about the localization here. There are several ways how to store the multi-language texts but I like Adam’s guide because it’s quite easy.

My MVCContrib-multi-language extension

In my project I have the source code of MVCContrib, so it was easy to extend. I demonstrate my extension on Labels.

I created 2 classes:

    1 namespace MvcContrib.UI.InputBuilder.Attributes

    2 {

    3     public class LabelAttribute : Attribute

    4     {

    5         public Type LabelResourceType { get; set; }

    6         public string LabelResourceName { get; set; }

    7 

    8         private string _label;

    9 

   10         public string Label

   11         {

   12             get

   13             {

   14                 if (!string.IsNullOrEmpty(_label))

   15                 {

   16                     return _label;

   17                 }

   18 

   19                 if (LabelResourceType != null && !string.IsNullOrEmpty(LabelResourceName))

   20                 {

   21                     PropertyInfo property = LabelResourceType.GetProperty(LabelResourceName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);

   22                     if (property != null)

   23                     {

   24                         var txt = property.GetValue(null, null) as string;

   25                         return txt;

   26                     }

   27                     return "Default:" + LabelResourceName;

   28                 }

   29 

   30                 return "";

   31             }

   32             set

   33             {

   34                 _label = value;

   35             }

   36         }

   37 

   38         public LabelAttribute()

   39         {

   40         }

   41 

   42         public LabelAttribute(string text)

   43         {

   44             Label = text;

   45         }

   46 

   47         public LabelAttribute(Type textResourceType, string textResourceName)

   48         {

   49             LabelResourceType = textResourceType;

   50             LabelResourceName = textResourceName;

   51         }

   52 

   53     }

   54 

   55     [AttributeUsage(AttributeTargets.Class)]

   56     public class LabelConventionAttribute : Attribute

   57     {

   58         public Type ResourceType { get; set; }

   59         public string Pattern { get; set; }

   60 

   61         public LabelConventionAttribute(Type resourceType, string pattern)

   62         {

   63             ResourceType = resourceType;

   64             Pattern = pattern;

   65         }

   66 

   67         public string GetLabelFor(string propertyName)

   68         {

   69             return new LabelAttribute(ResourceType, string.Format(Pattern, propertyName)).Label;

   70         }

   71     }

   72 }

Class LabelAttribute is to add the multilanguage support for the particular property. But because I’m lazy to write it on all properties of my model which is sent to the view, I create LabelConventionAttribute.

I like Convetion-over-Configuration style so using this attribute it is possible to setup the convention that the labels will be named by the convention Pattern.FormatWith(propetyName).

Next change was to change DefaultProperyConvention class to be partial. It’s due to the have my extensions in a separate class. I had to add there a method Lable

So here it’s

    1 namespace MvcContrib.UI.InputBuilder.Conventions

    2 {

    3     public partial class DefaultProperyConvention

    4     {

    5        

   17 

   18         public virtual string LabelForPropertyConvention(PropertyInfo propertyInfo)

   19         {

   20             if (propertyInfo.AttributeExists<LabelAttribute>())

   21             {

   22                 return propertyInfo.GetAttribute<LabelAttribute>().Label;

   23             }

   24             if (propertyInfo.ReflectedType.AttributeExists<LabelConventionAttribute>())

   25             {

   26                 return propertyInfo.ReflectedType.GetAttribute<LabelConventionAttribute>().GetLabelFor(propertyInfo.Name);

   27             }

   28             return propertyInfo.Name.ToSeparatedWords();

   29         }

   30     }

   31 }

   32 

And in the original DefaultPropertyConvention class I had to remove the method LabelForPropertyConvention.

That’s all. The similar steps can be done also for Description, but it’s necessary to extend PropertyViewModel class with new property.

The usage is:

    1 [DescriptionConvention(typeof(Descriptions), "Car_{0}")]

    2 [LabelConvention(typeof(Labels), "Car_{0}")]

    3 public class CarInput

    4 {

    5     [Required]

    6     [Example("BMW")]

    7     public virtual string Name { get; set; }

    8 }

Based on Adam’s guidance, I also had to create a resource file for the labels and the VS.NET created me the class Labels.

If you use MVCContrib builders then i.e. their Field.Master contains code like this: Html.Encode(Model.Label) to display the model’s label. Using the previously described extension it will be translated into the culture set in Thread.CurrentThread.CurrentUICulture.

That’s all for now …

Book Review: ASP.NET MVC 2 IN ACTION

I read MEAP version of the book ASP.NET MVC 2 in Action from Jeffrey Pallermo last month and I’d like to share my point of the view.

The book shows you how to use ASP.NET MVC 2 in Alt.Net (StuctureMap, MvcContrib, Rhino.Mocks, jQuery, NHibernate, NUnit, etc.) way – which I appreciate very much.

The book shows more how CodeCampServer.org was built connected with ASP.NET MVC 2, what is the logic, tips and tricks behind the designed solution. I must say, I like it as Headspring designed it. I just would call this book ASP.NET MVC 2 in Action on CodeCampServer project.

If you want to download and understand the code quite deeply, read this book. The quys around CodeCamperver are really cool ;o)!

If you want to know more about ASP.NET MVC 2 in general than how to use ASP.NET in Alt.NET, way then read this book: Pro ASP.NET MVC 2 Framework, Second Edition (Expert’s Voice in .NET)

SSL support in ASP.NET MVC

I have some views which I need to secure with SSL (HTTPS) and rest should be plain HTTP.  Thankfully, MVC 2 supports RequireSSL attribute to ensure the cuser is performing the action with HTTPS protocol. Fine! Cool!

But if the user is on the view secured by HTTPS then all links (ALL!) created by ActionLink extensions methods are secured! So the question is who/how and when to navigate back to the unsecured links/views?

I tought that the routes configuration is the part where to setup the routes and then we should just use route capabilities to create the links, redirects, etc. In other workds, the configuration is on 1 place and only once! and I can generate the links from all other places. If I want to change the links, I should change only route configuration and all should work well. And whole implemnetaiton should follow DRY principle. But it’s not true. I can’t specify that all links should be sent over HTTP except some special routes which should be handled by HTTPS.

I’ve read several sources how to implement it, i.e. http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ and Steve’s  implementation solves DRY principle but it overwrites VirtualPath to the absolute path and then the special context wrapper corrects it back.  I implemented it different way.

So what is my solution?

The idea behind the scene consists of 3 parts:

1) when configuring the routes, I add the data token scheme to the route definition

routes.Add(new Route("Account/Login", new MvcRouteHandler())
        {
            Defaults = new RouteValueDictionary(new { controller = "Account", action = "Login" }),
            DataTokens = new RouteValueDictionary(new { scheme = "https" })
        });

2) I had to implement and use my own ActionLink extentions methods called MyActionLink (hm, super name, isn’t?). MyActionLink methods are almost same as the default ActionLink implementation except the method with full parameter set.

public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, new RouteValueDictionary(), new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName)
        {
            return htmlHelper.MyActionLink(linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, routeValues, new RouteValueDictionary());
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
        {
            return htmlHelper.MyActionLink(linkText, actionName, null, routeValues, htmlAttributes);
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
        {
            return htmlHelper.MyActionLink(linkText, actionName, controllerName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes));
        }
        public static MvcHtmlString MyActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
        {
            if (string.IsNullOrEmpty(linkText))
            {
                throw new ArgumentNullException(linkText);
            }
            var requestContext = htmlHelper.ViewContext.RequestContext;
            var values = MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, new RouteValueDictionary(), true);
            var vpd = htmlHelper.RouteCollection.GetVirtualPath(requestContext, values);
            return htmlHelper.ActionLink(linkText, actionName, controllerName,
                (string)vpd.DataTokens["scheme"], (string)vpd.DataTokens["hostname"], null, routeValues, htmlAttributes);
        }

Note: The samilar changes can be done with RedirectToAction<T> and RedirectToAction methods in order to take care of “scheme”. But what I do is that I display the view that the secured action was done successfully and then the user is redirected to i.e. the index view of the controller.

3) decorate the appropriate action with RequireSSL attribute.

        [RequireHttps]
        [HttpPost]
        public ActionResult LogOn(LoginInput input)
        {
            ....
        }

And that’s all!

And you can use it as you would use ActionLink just with name MyActionLink.  So I replaced all ActionLink(s) to MyActionLink and that was all!

In my project I use MvcContrib. So I changed MvcRoute to include scheme “http” by default, all my links are created by MyActionLink extension method and sensitive actions are decorated with RequireSSL.

Do you know other, better implementation? Please, let me know.

Reaction to Dino’s comparism WebForms and MVC

I visited TechEd in Berlin this year with my colleagues and we had a discussion which web technology will we use in the big corporate env in near future. We worked with WebForms for several years and we (in Prague) did several projects with ASP.NET MVC. I muys say, I’m big fan of MVC because it’s finally trying to enables the developers to use the basic development principals like SoC, TDD, IoC/DI which has bug impacts on the code quality, application performance and maintenance! Personally, I think, MVC will be used in majority of the web apps developed in near future on .NET platform.

Anyway, my colleague spent some time searching the comparism and sent me a link from Dino Esposito where the author compares WebForms and ASP.NET MVC. I discussed this article with the colleagues who developed the web apps on MVC and we were really dissapointed with Dino’s article. I knew Dino as the author of ASP.NET web forms articles and based on the article he published on MSDN! about MVC I think, he was he paid by M$ to popularize WebForms over MVC with such junior-level facts or I dont know.

The problem is , Dino is quite know person and it takes a time to explain the people why this his article is so junior-level article selling WebForms. I took me really much time to explain people what was hidden in his article I’dlike to share it with you now.

Dino’s facts:

  • Web Forms allows you to learn as you go.

What? What does it mean? What weak fact is it? Did not ASP.NET MVC allow you to do the same? Come’on man, give me a break! To do the things correcntly (with the focus on QA, performance, etc) is hard! I dont want to use drag’n’drop M$ school, I dont want to use datasets or SQL data source with SQL in the aspx!  

  • Viewstate can be controlled or disabled.

Correctly disable complete viewstate/control state is quite hard work.

  • Web Forms was designed to abstract the Web machinery.

Is is advantage? I think, not! When devleoping apps on WEbForms I had to explain several times to the devleopers where the events occures (client or server?), etc. This abstraction hides many facts which web developers should be aware of!

Dino is presenting that you dont need to know HTML, CSS and JS to do the web forms. That’s the shame. I would compare it to the automechanic who doesn’t have knowledge how to fix the cars but he is fixing them. The result is bad quality. We, web developers, have to know HTML, CSS, JS and we need to have full control over them!

It’s funny how Dino’s presenting the comparism of the life cycles of MVC and WebForms. The page lifecycle is really hard to remember! It’s really hard to learn as you go! It would be fair to compare basic but full page lifecycles and you would see the difference. MVC is much simpler => much faster!

Honestly, I see the following main differences between WebForms and MVC:

  • MVC is more easy to understand and learn. After few years of the web forms developemnt I need to have the page lifecycle painted in the front of me to correctly do the things I want to achieve. It’s heavy and complicated! And I dont mention Viewstate/Controlstate nightmare! I think, MVC is much cleaner approach for the developers.
  • MVC was based in its core to follow TDD approach and it’s cool! Using WebForms it’s harder but you can use i.e. WCSF with MVP pattern.
  • MVC is much faster!  In MVC there is no needed overhead with pagelifecycle, etc. It’s very clear approach and it’s much faster!

There are 2 core requirements from our customers: I need to have good SW quality and I want to have fast application. MVC wins!

I think, MVC could miss the UI components. But this is not true because there are plenty of jQuery components which are free to use, you just need to set them the data and UI layout.

I visited Dino’s blog and he is presenting ASP.NET MVC and say that version 4.0 of ASP.NET is the dead-end! Hm, strange. On one side he is writing how web forms are cool and then next day he is presenting about MVC and he wants to write a book about MVC. No, thanks! There are better professionals than you who wrote about MVC (MVC in action, MVC unleashed).

At the end of the day I’d be very happy if people baid by M$ would inform/present topics more correctly and fairly. I take really too much time to explain other colleagues where the real benefits of the presented technologies are!

TT template for MVC Grid with ordering and paging

In my previous posts I wrote about my extensions to MVCContrib grid like paging and ordering.

It’s tedious work to create the pages with the grid and define all the columns by hand. As you know from MVC RC1 it’s possible to use the TT templates to gerenate the views. I found a TT template fofr MVC Grid somewhere on the web and I extended it to generate the views using my extentions for ordering an paging.

Here is a screenshot from the template:

mvccontrib

When you copy such template to the folder:

C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC\CodeTemplates\AddView

then you will be able to use it in the dialog:

mvc_addview

and here is the result:

mvc_addview_result

While generating the view using the entities which are derived from some base class which is not in the same assembly as the entity, you can get the exception “Could not load type …”. You need to copy the base assembly to the location “C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\\” and then it starts to work properly.

Using such template mechanism the developer can create the lists, details, etc. very quickly . The heart of the whole process is TT template. Just briefly, there is quite exhaustive blog here regarding TT templates.

The template can be found here

Ordering in the MVCcontrib grid

I miss an ordering functionality in the grid so I had to extend it using and extension method of HtmlHelper.

What I want to achieve
When a user comes to my grid, the url could be i.e.

http://www.site.com/cars/list

and when the user clicks a link in the header to order the data in the gird then the URL could be changed to

http://www.site.com/cars/list?orderby=name,true

In addition we should be able to inform the user about current ordering (asc,desc)  and the user should have possibility to click the link to change the ordering in the header.

In short something like this:

ordering

Current MVCContrib doesn’t include such support so I added it.

I made an extension methods which you can you like in the following:

ordering_code

OrerbyLink extension methods have to check the current URL to see what’s the current ordering. It would be nicer if this would be part of some
interface as well as it is done with IPagination.

I think, it would be quite good if IPagination would be renamed to ICollectionModel which could contain both:
1) info about the pagination
2) info about the ordering
3) collection of the data itself

And then it would be good if there would be an easy way who to wrap the custom IEnumarable<T> to ICollectionModel. Propable using an extension method as it’s doen via AsPagination<T>().

The code can be downloaded from here

What do you think?

ASP.NET MVC Pager

Based on the feedback I’ve got I decided to write more about the pager I wrote in recent days. I use this control with MVCContrib Grid control.

The main difference is that my Pager control doesn’t required the IPagination interface.

You can call it like this:

<%=Html.Pager(Model.Search.CurrentPageIndex, Model.Search.MaxRowsPerPage, Model.Search.TotalRowCount)%>

That’s all and it will look like the following picture:

pager

The code can be downloaded from here The link points to the ZIP file which contains 2 files, both are extensions. When you want to use the pager you havet o reference latest MVCContrib library as the pager links are built using MVCCongrib.UI.Tags.Link class.

When I use MVCContrib library I use to add the namespaces to the web.config.  Just FYI: you need to add to web.config the following:

<add namespace="MvcContrib.UI.Pager"/>

That’s all for now. Next will be ordering :o)