Why extension methods should not be used as part of a framework.

Two of my favorite frameworks/libraries uses extension methods heavily. Autofac uses extension methods during registration and ASP.NET MVC3 for their HTML Helpers. Since extension methods are static, they are closed for extension. Which are one of the most important principles for object oriented programming.

For MVC I would like to be able to add a tooltip automatically for all editors and labels. It’s really simple, I just want to add a “title” attribute to all generated HTML elements where the ModelMetadata contains a “Description”. The source code for LabelFor looks like this:

    public static class LabelExtensions {
        public static MvcHtmlString Label(this HtmlHelper html, string expression) {
            return LabelHelper(html,
                               ModelMetadata.FromStringExpression(expression, html.ViewData),
                               expression);
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) {
            return LabelHelper(html, 
                               ModelMetadata.FromLambdaExpression(expression, html.ViewData), 
                               ExpressionHelper.GetExpressionText(expression));
        }

        public static MvcHtmlString LabelForModel(this HtmlHelper html) {
            return LabelHelper(html, html.ViewData.ModelMetadata, String.Empty);
        }

        internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName) {
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText)) {
                return MvcHtmlString.Empty;
            }

            TagBuilder tag = new TagBuilder("label");
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
            tag.SetInnerText(labelText);
            return tag.ToMvcHtmlString(TagRenderMode.Normal);
        }
    }

Hence it’s impossible to do anything about the helper. I do understand why they use extension methods. It’s great since it’s very easy to find all available helpers. The problem is that you can’t do anything do modify the data that they generate.

The solution

Convert all extension methods to facades for the real implementation, and use DependencyResolver in the extension methods to find the proper classes. Create some interfaces and default implementations for all generators.

The new LabelHelper:

internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName)
{

    var labelGenerator = DependencyResolver.Resolve<ILabelGenerator>();
    return labelGenerator.Create(html, metadata, htmlFieldName);
}

The default implementation (simplified example):

	public class LabelGenerator : ILabelGenerator
	{
	
		public virtual void Create(HtmlHelper html, ModelMetadata metadata, string htmlFieldName)
		{
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (String.IsNullOrEmpty(labelText)) {
                return MvcHtmlString.Empty;
            }

            TagBuilder tag = new TagBuilder("label");
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName));
            tag.SetInnerText(labelText);
			ProcessTag(tag);
            return tag.ToMvcHtmlString(TagRenderMode.Normal);
		}
			
		protected virtual void ProcessTag(TagBuilder tag, ModelMetaData metaData)
		{
		}
	}

Which would allow me to create the following implementation:

	public class LabelWithTitlesGenerator : LabelGenerator
	{
		protected override void ProcessTag(TagBuilder tag, ModelMetaData metaData)
		{
			if (metaData.Description != null)
				tag.Attributes.Add("title", metaData.Description);
		}
	}

The only thing left is to inject it in my container and create a small jquery script which creates the actual tooltip using the “title” attribute.

Summary

Extension methods are closed for extension. Once declared you can not do anything to extend the functionality that they provide. It’s the same as if you would develop a framework where you put sealed on all your classes.

Extension methods were created to be able to make life easier by proving convenience methods for existing classes. The intention was not that you should use them as a part of your framework since you close down that part for extension if you do. The extension methods become locking out methods ;O

Easy model and validation localization in ASP.NET MVC3

Update I’ve written a more complete article about the framework at codeproject.com.


If you have googled a bit you’ll read that you should use DescriptionAttribute and DisplayNameAttribute to get localization which will result in a view model that looks something like this:

    public class UserViewModel
    {
        [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        [LocalizedDisplayName(ErrorMessageResourceName = "UserId", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        [LocalizedDescription(ErrorMessageResourceName = "UserIdDescription", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        public int Id { get; set; }

        [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        [LocalizedDisplayName(ErrorMessageResourceName = "UserFirstName", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        [LocalizedDescription(ErrorMessageResourceName = "UserFirstNameDescription", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        public string FirstName { get; set; }

        [Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        [LocalizedDisplayName(ErrorMessageResourceName = "UserLastName", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        [LocalizedDescription(ErrorMessageResourceName = "UserLastNameDescription", ErrorMessageResourceType = typeof(Resources.LocalizedStrings))]
        public string LastName { get; set; }
    }

Don’t do that.

The solution to get cleaner DataAnnotation localization is to derive the attributes and do some magic in the derived classes. The problem with that solution is that client-side validation stops working unless you create new adapters for your derived attributes.

Don’t do that.

The easy solution

I found an excellent post by Brad Wilson that goes through most of the new stuff in MVC3. It’s a must read. When reading it I came up with the idea to use the new Meta Data providers to do the localization. The solution works quite well and you’re models become clean again:

    public class UserViewModel
    {
        [Required]
        public int Id { get; set; }

        [Required]
        public string FirstName { get; set; }

        [Required]
        public string LastName { get; set; }
    }

You need to specify the new providers (created by me) in your global.asax:

        protected void Application_Start()
        {
            var stringProvider = new ResourceStringProvider(Resources.LocalizedStrings.ResourceManager);
            ModelMetadataProviders.Current = new LocalizedModelMetadataProvider(stringProvider);
            ModelValidatorProviders.Providers.Clear();
            ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider(stringProvider));
        }

That’s it. No more messy attributes.

Defining the localization resources

I created an interface called ILocalizedStringProvider which is used by both providers. This means that you do not have to use String Tables. Just create a new class and implement that interface to get support for your database, XML file or whatever you choose.

In my example I used a string table and the strings in it looks like this:

As you see, you should name your strings as “ModelName_PropertyName” and “ModelName_PropertyName_MetadataName” to include meta data like Watermark and NullDisplayText. Refer to MSDN to see which kind of meta data there is.

As for validation attributes, the string table names should simply be the attribute name without the “Attribute” suffix.

Code and documentation

The code is available at github.
Documentation is available here.

Update

Comments are now closed. Either open tickets at github or ask questions at stackoverflow.com with the tag “griffin.mvccontrib”. Write a comment starting with “@jgauffin” if I don’t answer within reasonable time 😉

Getting information into the Layout without using ViewBag.

You should avoid using the ViewBag as much as possible since you get run-time errors instead of compile time error if something fails. The problem is that it can be hard to avoid it if you are using information in your layout.

The solution that I’m using lets you do the following:

public class YourController : BaseController
{
    public ActionResult YourAction()
    {
        LayoutModel.MetaDescription = "Does something in the application";
        return View();
    }
}

And in your _Layout.cshtml:

<meta name="description" value="@LayoutModel.MetaDescription" />

See? Everything is typed.

The approach I’m using is to create a base class for views and inherit WebViewPage in it. First things first, add a model for your layout:

public class LayoutViewModel
{
    public string MetaKeywords { get; set; }
}

Then add the following to your BaseController:

public class BaseController : Controller
{
    private LayoutViewModel _layoutModel = new LayoutViewModel();

    public LayoutViewModel LayoutModel { get { return _layoutModel; } }

    protected override void OnResultExecuting(ResultExecutingContext ctx) 
    {
        var viewResult = ctx.Result as ViewResult;
        if (viewResult != null)
            viewResult.ViewBag.LayoutModel = LayoutModel;

        base.OnResultExecuting(ctx);
    }
}

Ok. I know. I’m cheating and using the ViewBag. But since it’s only in one place it’s less error prone than using the viewbag everywhere.

Now create the View base class. You need to create two versions to have support for typed views.

public abstract class ViewBaseWithLayoutModel : WebViewPage
{
    public LayoutViewModel LayoutModel { get { return (LayoutViewModel)ViewBag.LayoutModel; } }
}
public abstract class ViewBaseWithLayoutModel<T> : WebViewPage<T>
{
    public LayoutViewModel LayoutModel { get { return (LayoutViewModel)ViewBag.LayoutModel; } }
}

The last thing is to specify your new base view class in your ViewsWeb.config:

  <system.web.webPages.razor>
    <pages pageBaseType="Namespace.To.ViewBaseWithLayoutModel">

That’s it.

Making custom dataannotation attributes work with MVC client side validation

In a previous blog entry I showed you a easier approach to localized DataAnnotation attributes.

The problem with that approach was that client side validation didn’t work. It isn’t hard to fix. All you need is to borrow some code from the built in DataAnnotation support and reference the custom attributes instead.

Here is a sample implementation.

    public class RequiredAttributeAdapter : DataAnnotationsModelValidator<RequiredAttribute>
    {
        public RequiredAttributeAdapter(ModelMetadata metadata, ControllerContext context, RequiredAttribute attribute)
            : base(metadata, context, attribute)
        {
        }

        public static void SelfRegister()
        {
            DataAnnotationsModelValidatorProvider
                .RegisterAdapter(
                    typeof (RequiredAttribute),
                    typeof (RequiredAttributeAdapter));
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            return new[] { new ModelClientValidationRequiredRule(ErrorMessage) };
        }
    }

Then simply call RequiredAttributeAdapter.SelfRegister(); somewhere in your Global.asax

Three reasons to why you should use view models

I’m answered a couple of questions at stackoverflow regarding the benefit of view models. Here is a summary of all my answers focusing on view models in ASP.NET MVC.

Reason 1: Remove logic from your views.

When you start working with ASP.NET MVC you’ll most likely ask yourself why you should use a view model. Using your domain model or entity model works perfectly fine. And it does. For a while. But as you continue to use your models you’ll discover that you have to add some adaptations in your views. Here is a typical usage adaptation (using razor view engine):

Hello @model.UserName

Your age: @(model.Age != 0 ? model.Age.ToString() : "n/a")

Not so bad, is it? The problem is that you have introduced logic into your view, which is bad for two reasons:

A) You can not unit test that code, and the only way to make sure that it works is user testing.
B) You need to repeat that code for every view that intends to use your model (code duplication = code smell)

All those small adaptations will lead to a big mess eventually.

Reason two: Security

One of the biggest advantages with view models are to remove security risks. Your database objects or domain objects will most likely contain properties that the user should not be able to change. It can be a property called IsAdmin or Reputation.

All those properties will automatically be changed by the model binder if they exist in the model (and are posted in the FORM which is quite easy to do if you know HTML). Simply remove them from the view model and they’ll never be changed.

Reason three: Loose coupling

By using domain models or entity models you are adding coupling between your lower layers and the presentation layer and that is seldom good. Google “loose coupling” to find out more.

Basically it means that if you change your domain/entity model you have to change all your views that use that model. If you use a ViewModel you only have to change the code that maps between an entity/domain model and the view model.

How to make the model mapping easier

I usually use a framework called AutoMapper to manage the mappings between viewmodels and other models. AutoMapper and it’s alternatives usually let yuo use a single line of code to do the mapping.

Render a view to a string variable

It’s sometimes useful to render a view or a partial to a string variable instead of returning an ActionResult. the following code does just that, add it to your base controller.

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    ViewData.Model = model;

    using (StringWriter sw = new StringWriter()) {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);

        return sw.GetStringBuilder().ToString();
    }
}

HtmlHelper for enums in ASP.Net MVC3

Have you tried to get enums working with HtmlHelpers? It can be a hassle and here is my solution for it. It requires that you tag each entry in the enum with a [Description] attribute.

public static class SelectExtensions
{

    public static string GetInputName<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        if (expression.Body.NodeType == ExpressionType.Call)
        {
            MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
            string name = GetInputName(methodCallExpression);
            return name.Substring(expression.Parameters[0].Name.Length + 1);

        }
        return expression.Body.ToString().Substring(expression.Parameters[0].Name.Length + 1);
    }

    private static string GetInputName(MethodCallExpression expression)
    {
        // p => p.Foo.Bar().Baz.ToString() => p.Foo OR throw...
        MethodCallExpression methodCallExpression = expression.Object as MethodCallExpression;
        if (methodCallExpression != null)
        {
            return GetInputName(methodCallExpression);
        }
        return expression.Object.ToString();
    }

    public static MvcHtmlString EnumDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
    {
        string inputName = GetInputName(expression);
        var value = htmlHelper.ViewData.Model == null
            ? default(TProperty)
            : expression.Compile()(htmlHelper.ViewData.Model);

        return htmlHelper.DropDownList(inputName, ToSelectList(typeof(TProperty), value.ToString()));
    }

    public static SelectList ToSelectList(Type enumType, string selectedItem)
    {
        List<SelectListItem> items = new List<SelectListItem>();
        foreach (var item in Enum.GetValues(enumType))
        {
            FieldInfo fi = enumType.GetField(item.ToString());
            var attribute = fi.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
            var title = attribute == null ? item.ToString() : ((DescriptionAttribute)attribute).Description;
            var listItem = new SelectListItem
                {
                    Value = ((int)item).ToString(),
                    Text = title,
                    Selected = selectedItem == ((int)item).ToString()
                };
            items.Add(listItem);
        }

        return new SelectList(items, "Value", "Text");
    }
}

Usage:

@Html.EnumDropDownListFor(m => m.SubscriptionMode);

If you want to be able to use it for validation, you need to be able to have a “undefined” entry in your enum and do the following change:

// replace "Value = ((int)item).ToString()," in to select list.
Value = (int)item == 0 ? string.Empty : ((int)item).ToString(),