More on Fluent MetadataProvider for ASP.NET MVC

In my last post, one of the thing you complained about maintaining view model and its meta data in two separate place and I admit this becomes a pain when you browse through the codes. I have changed the  implementation, so now you will be able to configure the meta data very much like the way you do in Fluent NHibernate or Entity Framework 4.0 Code only version (thanks to Mohamed Meligy from brining it up). There has been also two additional  features that I have added, built-in support to show DropDownList/Select element and implicitly adding Email/Url regular expression validation when you apply those in string property.

The following shows both the DataAnnotations and the Fluent version:

DataAnnotations:

public class ProductEditModel
{
    [ScaffoldColumn(false)]
    public int Id { get; set; }

    [Required(ErrorMessage = "Name cannot be blank.")]
    [StringLength(64, ErrorMessage = "Name cannot be more than 64 characters.")]
    public string Name { get; set; }

    [DisplayName("Category")]
    [Required(ErrorMessage = "Category must be selected.")]
    public Category Category { get; set; }

    [DisplayName("Supplier")]
    [Required(ErrorMessage = "Supplier must be selected.")]
    public Supplier Supplier { get; set; }

    [Required(ErrorMessage = "Price cannot be blank.")]
    [Range(10, 1000, ErrorMessage = "Price must be between 10.00-1000.00.")]
    [DataType(DataType.Currency)]
    public decimal Price { get; set; }
}

Fluent:

public class ProductEditModel
{
    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

    public Supplier Supplier { get; set; }

    public decimal Price { get; set; }
}

public class ProductEditModelConfiguration : ModelMetadataConfigurationBase<ProductEditModel>
{
    public ProductEditModelConfiguration()
    {
        Configure(model => model.Id).Hide();
        Configure(model => model.Name).Required("Name cannot be blank.").MaximumLength(64, "Name cannot be more than 64 characters.");
        Configure(model => model.Category).DisplayName("Category").Required("Category must be selected.").DropDownList("categories", "[Select category]");
        Configure(model => model.Supplier).DisplayName("Supplier").Required("Supplier must be selected.").DropDownList("suppliers", "[Select supplier]");
        Configure(model => model.Price).AsCurrency().Required("Price cannot be blank.").Range(10, 1000, "Price must be between 10.00-1000.00.");
    }
}

Nothing special, all you have to do is create a class that inherits from ModelMetadataConfigurationBase and pass your view model class as generic definition, then configure the properties. And you no longer have to register it with the BootstrapperTaskBase like you did before, the System.Web.Mvc.Extensibility is intelligent enough to auto register those.

What do you think, again comments and suggestion are greatly appreciated.

Download: github

Shout it

15 Comments

  • this is very interseting. I am not crazy about the dropdown list. I would also like to see the required reverese... so that properties are required by default and then add an option method to remove the required field.

    Very cool stuff.

  • Thanks Eric.

    Did you mean a NotRequired() method?

  • Yes.. .NotRequired() or .Optional() somthing like that.

  • @eric : Added both optional -> Required and Writable ->Readonly :-). Check the Github.

  • I do something very similar on a little project of mine. One thing I do is on my equivalents of MaximumLength and Range is automatically format the description string using the length or range as parameters. i.e.
    .Range(10, 1000, "Price must be between {0:0.00}-{1:0.00}.");
    or
    .MaximumLength(64, "Name cannot be more than {0} characters.")
    If you are going to allow localization of the messages this prevents a simple change of length becoming a bigger job than it need be.

    Regards,
    Paul

  • @Paul : It does have support of the message formatting with parameter values as internally it uses the same DataAnnotations validators. I will be implementing the localization for the next drop.

  • Now that's what i call improvement. No more complains. :)

  • @Arnis : Thanks ;-).

  • I can see already how i would structure my 'perfect viewmodel'. Everything (automapper mappings, metadataconfigurations) nicely nested together with corresponding entity view model using this 'trick'=>

    http://mikehadlow.blogspot.com/2006/11/nested-files-with-dependentupon-in.html

    One last thing - why ModelMetadataConfigurationBase? Why not just ModelMetadataConfiguration? Maybe ModelMetadata is available? What about ModelMetadataMap? :)

  • I think it is a standard practice to suffix the abstract class with "Base", though there is exception like Controller. Control etc etc.

    In FluentNH it is actually mapping to db tables, but over here we are not mappiing instead we are specifying the configuration of the view model how it will appear in the screen.

  • @Arnis:

    I think it is a standard practice to suffix the abstract class with "Base", though there are exceptions like Controller. Control etc etc.

    In FluentNH it is actually mapping to db tables, but over here we are not mapping instead we are specifying the configuration of the view model how it will appear in the screen.

  • It would be nice to have a minimum string length as well.

  • Like the improvements. I'm interested to see L10N incorporated.

    If I find the time I will give this a spin in my pet project to test the real world practice of it.

  • Hi Kazi,

    Great stuff, this has quickly become one of my favorite blogs on the old RSS feed.

    Not intending to derail the topic of the blog post, just wanted to chime in regarding one of your comments:

    "I think it is a standard practice to suffix the abstract class with "Base", though there is exception like Controller. Control etc etc."

    From the .NET Framework Design Guidelines book:

    Avoid naming base classes with a "Base" suffix if the class is intended for use in public APIs.

    Krzysztof also adds some additional reasoning, which actually don't apply that much to your particular API, but still good information.

    http://blogs.msdn.com/kcwalina/archive/2005/12/16/BaseSuffix.aspx

    Anyway just food for thought, keep up the good work.

    -Matt

  • Thanks Matt for pointing that out.

    "Old habits die hard" ;-)

Comments have been disabled for this content.