Thursday, December 24, 2009 8:32 AM
Kazi Manzur Rashid
Introducing Fluent MetadataProvider for ASP.NET MVC
I have just included a Fluent Metadata provider in my open source System.Web.Mvc.Extensibility project. Currently it contains all of the features of the Built-in DataAnnotations Metadata provider that comes with the ASP.NET MVC 2 framework. Consider it as a holiday special from me for the ASP.NET MVC community :-).
The main reason I am not fond of the DataAnnotations provider is, it is completely Attribute based, In my opinion, attributes adds extra noise to the code which I always tried to avoid, I love pure POCO. Beside the noise, the most important drawback of using attribute is that there is no conventional way to inject your dependencies into your attribute by your preferred IoC container and DataAnnotations is no exception in this case. Another issue that is also true for the attribute based model is that you are always free to decorate your code with invalid attributes and there is no compile type checking, For example, in this case nothing going to stop you to add StringLength or data type DateTime attribute to a integer based property.
These are the main driving force to come up with my Fluent Medata provider.
Now, lets take a quick look how to define the metadata for the model with the fluent metadata provider, but, first lets see the DataAnnotations attributed based model of the sample application:
public class ProductDisplayModel
{
[ScaffoldColumn(false)]
public int Id { get; set; }
public string Name { get; set; }
public string CategoryName { get; set; }
public string SupplierName { get; set; }
[DataType(DataType.Currency)]
public decimal Price { get; set; }
}
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; }
[ScaffoldColumn(false)]
public SelectList Categories { get; set; }
[ScaffoldColumn(false)]
public SelectList Suppliers { get; set; }
}
For the fluent version, you first have to create a class which inherits from the BootstrapperTaskBase so that the System.Web.Mvc.Extensibility framework can automatically register it when the application starts, the following is the complete code which would work exactly same as the above:
public class RegisterModelMetadata : BootstrapperTaskBase
{
protected override void ExecuteCore(IServiceLocator serviceLocator)
{
IModelMetadataRegistry registry = serviceLocator.GetInstance<IModelMetadataRegistry>();
registry.Register<ProductDisplayModel>(configurator =>
{
configurator.Configure(model => model.Id).Hide();
configurator.Configure(model => model.Price).AsCurrency();
})
.Register<ProductEditModel>(configurator =>
{
configurator.Configure(model => model.Id).Hide();
configurator.Configure(model => model.Name).Required("Name cannot be blank.").MaximumLength(64, "Name cannot be more than 64 characters.");
configurator.Configure(model => model.Category).DisplayName("Category").Required("Category must be selected.");
configurator.Configure(model => model.Supplier).DisplayName("Supplier").Required("Supplier must be selected.");
configurator.Configure(model => model.Price).AsCurrency().Required("Price cannot be blank.").Range(10, 1000, "Price must be between 10.00-1000.00.");
configurator.Configure(model => model.Categories).Hide();
configurator.Configure(model => model.Suppliers).Hide();
});
ModelMetadataProviders.Current = new ExtendedModelMetadataProvider(registry);
}
}
You can find the complete example in the sample folder.
When configuring the model you will find that it gives you the options that are only applicable for that data type, for example, you cannot apply string length constraint to an integer property, currency is only valid for the decimal type etc etc. It also has a nice extensibility model to add more option which I will cover in the future blog post.
What do you think? Comments are suggestions are greatly appreciated.
Download: github
Filed under: Asp.net, MVC, ASPNETMVC, ASP.NET MVC, Open Source