ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1

Oct 3rd, 2009 | By | Category: Web Apps

While going through the new features of ASP.NET MVC 2 Preview 2, one feature seemed quite interesting to me.There are a set of classes, using which we can build a custom validation framework for the model.ASP.NET MVC framework by default provides a Data Annotation(classes present in System.ComponentModel.DataAnnotations namespace) based  model validation.This makes use of attributes added in the model class.But this can be overridden by custom implementations with great ease.In the next series of posts I will discuss about the main classes related to model validation along with the sample code that I have written to build a XML config based validator.

First let us discuss about ModelMetaData and ModelMetaDataProvider classes in System.Web.Mvc namespace.

  • ModelMetaData – This class represents the metadata for a particular model class/property.While implementing custom metadata validator this can be extended to store any additional metadata.
  • ModelMetaDataProvider – This is the abstract base class for metadata provider which needs to be implemented by the custom metadata providers.The three abstract methods are
    • public abstract IEnumerable<ModelMetadata> GetMetadataForProperties(Object container,Type containerType) – This class retrieves metadata for the properties of a model.
    • public abstract ModelMetadata GetMetadataForProperty(Object model,Type containerType,string propertyName)- This class retrieves metadata for a property of a model.
    • public abstract ModelMetadata GetMetadataForType(Object model,Type modelType) – This class retrieves metadata for the class of a model.

I have developed a sample application to explore these classes.These sample validator uses a xml file to store metadata information as shown below.To keep it simple for a sample for every property I have only  defined validator of type Required along with corresponding error message.

<?xml version="1.0" encoding="utf-8" ?>
<models>
  <model type="SB.App.Books.Models.Book">
    <properties>
      <property name="Title" datatype="System.String">
        <validators>
          <validator type="Required" message="Title cannot be blank"/>
        </validators>
      </property>
      <property name="Author" datatype="System.String">
        <validators>
          <validator type="Required" message="Author cannot be blank"/>
        </validators>
      </property>
      <property name="Publisher" datatype="System.String">
        <validators>
          <validator type="Required" message="Publisher cannot be blank"/>
        </validators>
      </property>
    </properties>
  </model>
</models>

I have used the following classes as an abstraction of this configuration data.

    /// <summary>
    /// Provide abstraction for the "model" node
    /// </summary>
    public class ModelConfig
    {
        public string ModelType {get;set;}
        public Dictionary<string,PropertyConfig> PropertyConfigs { get; set; }
    }
    /// <summary>
    /// Provide abstraction for the "propertynode" node
    /// </summary>
    public class PropertyConfig
    {
        public string Name { get; set; }
        public Type  DataType { get; set; }
        public List<ValidatorConfig> ValidatorConfigs { get; set; }
    }
    /// <summary>
    /// Provide abstraction for the "validatornode" node
    /// </summary>
    public class ValidatorConfig
    {
        public string Type { get; set; }
        public string Message { get; set; }
    }

There is a class called ModelConfigManager which reads the config file and initializes and caches the configuration data in an Dictionary object as shown below:

  public class ModelConfigManager
    {
        //Stores the Config Information

        private static Dictionary<string, ModelConfig> Config = new Dictionary<string, ModelConfig>();

        public static string ConfigLocation { get; set; }
        /// <summary>
        /// Reads and Initializes The Config Object Model
        /// </summary>
        public static void Initialize()
        {
            ModelConfig modelconfig = null;
            PropertyConfig propertyconfig = null;
            ValidatorConfig validatorconfig = null;
            XElement config = XElement.Load(ConfigLocation);
            var models = from el in config.Elements()
                         select el;
            foreach(var model in models)
            {
                modelconfig = new ModelConfig();
                modelconfig.ModelType = model.Attribute("type").Value;
                modelconfig.PropertyConfigs = new Dictionary<string, PropertyConfig>();
                foreach (var property in model.Descendants("properties").Descendants<XElement>("property"))
                {
                    propertyconfig = new PropertyConfig();
                    propertyconfig.Name = property.Attribute("name").Value;
                    propertyconfig.DataType = Type.GetType(property.Attribute("datatype").Value);
                    propertyconfig.ValidatorConfigs = new List<ValidatorConfig>();
                    foreach (var validator in property.Descendants("validators").Descendants<XElement>("validator"))
                    {
                        validatorconfig = new ValidatorConfig();
                        validatorconfig.Type = validator.Attribute("type").Value;
                        validatorconfig.Message = validator.Attribute("message").Value;
                        propertyconfig.ValidatorConfigs.Add(validatorconfig);
                    }

                    modelconfig.PropertyConfigs.Add(propertyconfig.Name, propertyconfig);
                }
                Config.Add(modelconfig.ModelType, modelconfig);
            }
        }
        /// <summary>
        /// Returns Model Configuration for a given type
        /// </summary>
        /// <param name="modelType">Model Type</param>
        /// <returns>Model Configuration</returns>
        public static ModelConfig GetModelConfig(string modelType)
        {

                return Config[modelType];
        }
    }

The class ValidationMetaData stores the type of validation and error message.The ValidatorConfig class could have been reused.But to keep the metadata and config hierarchies separate I have created this one.This can be refactored later.

    public class ValidationMetaData
    {
        public string ValidationType { get; set; }
        public string ErrorMessage { get; set; }
    }

ConfigModelMetaData is the custom metadata class which extends ModelMetaData class.Since one property can have multiple validations it maintains a reference to List of ValidationMetaData objects as shown below:

    public class ConfigModelMetaData : ModelMetadata
    {
        public List<ValidationMetaData> Validations { get; set; }
        public ConfigModelMetaData(ModelMetadataProvider provider,
                                   Type containerType,
                                   Func<object> model,
                                   Type modelType,
                                   string propertyName,
                                   List<ValidationMetaData> validations
            ): base (provider,containerType,model,modelType,propertyName)
        {
            this.Validations = validations;
        }

    }

The ConfigModelMetaDataProvider is the custom model metadata provider class.It reads the config values from ModelConfigManager cache and creates ConfigModelMetaData objects as shown below:

   public class ConfigModelMetaDataProvider : ModelMetadataProvider
    {
        public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
        {
           foreach(PropertyInfo property in containerType.GetProperties())
           {
               //Get MetaData for a single property
               yield return GetMetadataForProperty(new Func<object>(()=>property.GetValue(container,null)), containerType, property.Name);
           }
        }

        public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
        {
            ValidationMetaData valmetadata = null;
            List<ValidationMetaData> valmetadatalist = null;
            //Get Config Value for a property
            PropertyConfig propconfig = ModelConfigManager.GetModelConfig(containerType.FullName)
                                                                .PropertyConfigs[propertyName];
            foreach(ValidatorConfig valconfig in propconfig.ValidatorConfigs)
            {
                valmetadata = new ValidationMetaData();
                valmetadata.ValidationType = valconfig.Type;
                valmetadata.ErrorMessage=valconfig.Message;
                if(valmetadatalist==null) valmetadatalist = new List<ValidationMetaData>();
                valmetadatalist.Add(valmetadata);
            }
            //Create metadata for a property
            ConfigModelMetaData configmetadata = new ConfigModelMetaData
                                                (this, containerType, modelAccessor,
                                                 propconfig.DataType, propertyName,
                                                 valmetadatalist);
            return configmetadata;
        }

        public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType)
        {
            return new ConfigModelMetaData(this, null, modelAccessor,
                                           modelType, null,
                                           null);
        }
    }

The framework class ModelMetaDataProviders class exposes the property Current.This by default refers to DataAnnotationModelMetadataProvider.This needs to be changed in order to plugin the custom model metadata provider as shown below:

 ModelMetadataProviders.Current = new ConfigModelMetaDataProvider();

In the next post we will discuss about developing custom ModelValidator and ModelValidator provider.


Kick It on DotNetKicks.com
Tags:
  • http://aspdotnetmvc.com/buzz/default.aspx ASP.NET MVC Archived Buzz, Page 1

    [...] to Vote[FriendFeed] ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design (10/3/2009)Saturday, October 03, 2009 from [...]

  • http://topsy.com/tb/is.gd/3UuDb Tweets that mention ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design — Topsy.com

    [...] This post was mentioned on Twitter by Iftekhar Ahmed Amit. Iftekhar Ahmed Amit said: RT: @scottgu: Another blog that describes creating a custom validation provider & integrating with ASP.NET MVC 2: http://tinyurl.com/y9cwtvu [...]

  • http://codingndesign.com/blog/?p=96 ModelValidator in ASP.NET MVC 2 Preview 2 – Part 2 – Coding N Design

    [...] you might want to subscribe to the RSS feed for updates on this topic.Powered by WP Greet BoxIn my last post we have discussed about the ModelMetaData and related classes along with a sample code.In this post [...]

  • http://scriptsrss.com/modelvalidator-in-aspnet-mvc-2-preview-2-%e2%80%93-part-1-coding-n-design/ ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design Scripts Rss

    [...] here to see the original:  ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design By admin | category: asp.net script | tags: custom-validation, going-through, model, mvc, [...]

  • http://sovit.biz/modelvalidator-in-aspnet-mvc-2-preview-2-%e2%80%93-part-1-coding-n-design/ ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design

    [...] here:  ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design SHARETHIS.addEntry({ title: "ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N [...]

  • http://www.netdeluxo.com/blog/programming/asp-programming/modelvalidator-in-asp-net-mvc-2-preview-2-%e2%80%93-part-1-coding-n-design/ ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design | Webmaster Tools

    [...] Continued here: ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – Coding N Design [...]

  • Haacked

    Great post. By the way, you linked to my ASP.NET MVC v1.0 Preview 2 post. You should link to the ASP.NET MVC 2 Preview 2 post. :) http://haacked.com/archive/2009/10/01/asp.net-mvc-preview-2-released.aspx

  • Haacked

    Great post. By the way, you linked to my ASP.NET MVC v1.0 Preview 2 post. You should link to the ASP.NET MVC 2 Preview 2 post. :) http://haacked.com/archive/2009/10/01/asp.net-mvc-preview-2-released.aspx

  • http://blog.cwa.me.uk/2009/10/05/the-morning-brew-447/ Reflective Perspective – Chris Alcock » The Morning Brew #447

    [...] ModelValidator in ASP.NET MVC 2 Preview 2 – Part 1 – ’sankarsan’ starts a series exploring the model validation features present in the latest ASP.NET MVC 2 Preview 2. This first part looks at the Model Metadata and Model Metadata Provider interface, and Part 2 continues with a look at the Model Validator and ModelValidator Provider [...]

  • Johan Nilsson

    Great post indeed!
    Interesting reading, going to dive right into part 2 now : )

    Cheers

  • Johan Nilsson

    Great post indeed!
    Interesting reading, going to dive right into part 2 now : )

    Cheers