Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Want to change the Validation attribute at runtime ASP.Net MVC

Want to change the Validation attribute at runtime ASP.Net MVC

Scheduled Pinned Locked Moved C#
helpasp-netcsharparchitecturequestion
15 Posts 3 Posters 19 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • I indian143

    Hi all friends, I have a Model which is needed to have different validation attributes assigned to it in different Conditions, I don't have to create multiple classes for different attributes, because the same model class is being used in multiple post methods. Following is my class:

    public class ServiceFunctionCategoryLKP
    {
        public int Id { get; set; }
    
        \[Required(ErrorMessage = "Code is required.")\]
        \[StringLength(2, MinimumLength = 2, ErrorMessage = "The field {0} length must be at least 2.")\]
        public string Code { get; set; }
    
        \[Required(ErrorMessage = "Description is required.")\]
        public string Description { get; set; }
        
        public string ServiceFunctionCategoryCode { get; set; }
    
        \[Required(ErrorMessage = "ModeOfServiceCode is required.")\]
        public string ModeOfServiceCode { get; set; }
        public string ModeOfServiceDescription { get; set; }
        public int CodeLenth {get; set;}
    }
    

    StringLength attribute should be different for different calls, is there any way I can have set it differently or dynamically depending upon the input object of the action method. Means I have a field in my input object as above called CodeLength, depending upon this field I have to change the StringLength attributes MinimumLength and Error message properties dynamically, it means in every different Post call, it could be different depending upon the CodeLength field value. If I have tried in the following way, with IsValid method, problem is how would I know CodeLength field (that is coming as a property of Object in action method) within IsValid method to compare the length of the field with CodeLength field.

    public override bool IsValid(object value)
    {
    int num = (value == null) ? 0 : ((string)value).Length;
    return value == null || (num >= this.MinimumLength && num <= this.MaximumLength);
    }

    Any help would be greatly greatly helpful, thanks in advance friends. Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

    Richard DeemingR Offline
    Richard DeemingR Offline
    Richard Deeming
    wrote on last edited by
    #4

    Not easily, but it can be done. You're going to need to create a custom DataAnnotationsModelValidatorProvider, and modify the rules it returns. validation - Possible to change Data Annotations during Runtime? (ASP.NET MVC's [Range] [Required] [StringLength] etc.) - Stack Overflow[^] Eg:

    public class CustomModelValidatorProvider : DataAnnotationsModelValidatorProvider
    {
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
    if (metadata.Model is ServiceFunctionCategoryLKP model && metadata.PropertyName == nameof(model.Code) && model.CodeLength >= 0)
    {
    var newAttributes = new List<Attribute>(attributes);
    var stringLength = newAttributes.OfType<StringLengthAttribute>().FirstOrDefault();
    if (stringLength != null)
    {
    newAttributes.Remove(stringLength);

                if (model.CodeLength != 0)
                {
                    newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                    {
                        MinimumLength = model.CodeLength, 
                        ErrorMessage = $"The field {{0}} length must be at least {model.CodeLength}."
                    });
                }
                
                attributes = newAttributes;
            }
        }
        
        return base.GetValidators(metadata, context, attributes);
    }
    

    }

    Register in Globals.asax.cs:

    ModelValidatorProviders.Providers.Clear();
    ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() );

    NB: You're effectively allowing the user to override the validation rule by modifying a hidden field on the page. As a result, the validation can't really be trusted. It would be better to create a separate model class for each required code length.


    "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

    "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

    I 1 Reply Last reply
    0
    • Richard DeemingR Richard Deeming

      Not easily, but it can be done. You're going to need to create a custom DataAnnotationsModelValidatorProvider, and modify the rules it returns. validation - Possible to change Data Annotations during Runtime? (ASP.NET MVC's [Range] [Required] [StringLength] etc.) - Stack Overflow[^] Eg:

      public class CustomModelValidatorProvider : DataAnnotationsModelValidatorProvider
      {
      protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
      {
      if (metadata.Model is ServiceFunctionCategoryLKP model && metadata.PropertyName == nameof(model.Code) && model.CodeLength >= 0)
      {
      var newAttributes = new List<Attribute>(attributes);
      var stringLength = newAttributes.OfType<StringLengthAttribute>().FirstOrDefault();
      if (stringLength != null)
      {
      newAttributes.Remove(stringLength);

                  if (model.CodeLength != 0)
                  {
                      newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                      {
                          MinimumLength = model.CodeLength, 
                          ErrorMessage = $"The field {{0}} length must be at least {model.CodeLength}."
                      });
                  }
                  
                  attributes = newAttributes;
              }
          }
          
          return base.GetValidators(metadata, context, attributes);
      }
      

      }

      Register in Globals.asax.cs:

      ModelValidatorProviders.Providers.Clear();
      ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() );

      NB: You're effectively allowing the user to override the validation rule by modifying a hidden field on the page. As a result, the validation can't really be trusted. It would be better to create a separate model class for each required code length.


      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

      I Offline
      I Offline
      indian143
      wrote on last edited by
      #5

      Just checking, is it going to remove all the other Validations that I have put on other classes? Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

      Richard DeemingR 1 Reply Last reply
      0
      • I indian143

        Just checking, is it going to remove all the other Validations that I have put on other classes? Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

        Richard DeemingR Offline
        Richard DeemingR Offline
        Richard Deeming
        wrote on last edited by
        #6

        No. It checks that the instance being validated is the specific model class; that the property being validated is the Code property; and it only replaces the StringLength validator on that property.


        "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

        "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

        I 2 Replies Last reply
        0
        • Richard DeemingR Richard Deeming

          No. It checks that the instance being validated is the specific model class; that the property being validated is the Code property; and it only replaces the StringLength validator on that property.


          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

          I Offline
          I Offline
          indian143
          wrote on last edited by
          #7

          OK Got it, and you are saying that there is just one risk in it that it can be overridden in the derived classes, understandable, thanks a lot Richard for giving me the information. Thank you very much have a wonderful weekend buddy. Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

          1 Reply Last reply
          0
          • Richard DeemingR Richard Deeming

            No. It checks that the instance being validated is the specific model class; that the property being validated is the Code property; and it only replaces the StringLength validator on that property.


            "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

            I Offline
            I Offline
            indian143
            wrote on last edited by
            #8

            Hi Richard, One last thing, I have created the method in the following way:

            public class CustomModelValidatorProvider : System.Web.Mvc.DataAnnotationsModelValidatorProvider
            {
                protected override IEnumerable GetValidators(System.Web.Mvc.ModelMetadata metadata, ControllerContext context, IEnumerable attributes)
                { 
                    if (metadata.Model is LookupTable)
                    {
                        LookupTable model = metadata.Model as LookupTable;
            
                        if (metadata.PropertyName == GetPropertyName(() => model.CodeLength) && (model.CodeLength > 0))
                        {
                            var newAttributes = new List(attributes);
                            var stringLength = newAttributes.OfType().FirstOrDefault();
                            if (stringLength != null)
                            {
                                newAttributes.Remove(stringLength);
            
                                if (model.CodeLength != 0)
                                {
                                    newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                                    {
                                        MinimumLength = model.CodeLength,
                                        ErrorMessage = @"The field {{0}} length must be at least {model.CodeLength}."
                                    });
                                }
            
                                attributes = newAttributes;
                            }
                        }
                    }            
            
                    return base.GetValidators(metadata, context, attributes);
                }
            
                public string GetPropertyName(Expression\> propertyLambda)
                {
                    var me = propertyLambda.Body as MemberExpression;
            
                    if (me == null)
                    {
                        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
                    }
            
                    return me.Member.Name;
                }
            }
            

            can you please let me know how can I decorate or use it on my Model class, can you please give me that example, I am asking this question because this value is coming as null: metadata.PropertyName, so I am not able to compare with property name. So any help please? thank you very very much buddy - as I said earlier have a Wonderful weekend with family and friends buddy. Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

            Richard DeemingR 1 Reply Last reply
            0
            • I indian143

              Hi Richard, One last thing, I have created the method in the following way:

              public class CustomModelValidatorProvider : System.Web.Mvc.DataAnnotationsModelValidatorProvider
              {
                  protected override IEnumerable GetValidators(System.Web.Mvc.ModelMetadata metadata, ControllerContext context, IEnumerable attributes)
                  { 
                      if (metadata.Model is LookupTable)
                      {
                          LookupTable model = metadata.Model as LookupTable;
              
                          if (metadata.PropertyName == GetPropertyName(() => model.CodeLength) && (model.CodeLength > 0))
                          {
                              var newAttributes = new List(attributes);
                              var stringLength = newAttributes.OfType().FirstOrDefault();
                              if (stringLength != null)
                              {
                                  newAttributes.Remove(stringLength);
              
                                  if (model.CodeLength != 0)
                                  {
                                      newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                                      {
                                          MinimumLength = model.CodeLength,
                                          ErrorMessage = @"The field {{0}} length must be at least {model.CodeLength}."
                                      });
                                  }
              
                                  attributes = newAttributes;
                              }
                          }
                      }            
              
                      return base.GetValidators(metadata, context, attributes);
                  }
              
                  public string GetPropertyName(Expression\> propertyLambda)
                  {
                      var me = propertyLambda.Body as MemberExpression;
              
                      if (me == null)
                      {
                          throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
                      }
              
                      return me.Member.Name;
                  }
              }
              

              can you please let me know how can I decorate or use it on my Model class, can you please give me that example, I am asking this question because this value is coming as null: metadata.PropertyName, so I am not able to compare with property name. So any help please? thank you very very much buddy - as I said earlier have a Wonderful weekend with family and friends buddy. Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

              Richard DeemingR Offline
              Richard DeemingR Offline
              Richard Deeming
              wrote on last edited by
              #9

              You register the provider in Global.asax.cs:

              ModelValidatorProviders.Providers.Clear();
              ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() );

              I'd expect to see the PropertyName set to null for the class-level validation, but I'd expect the method to be called again for each property on the class. You're currently checking whether the validators are for the CodeLength property; you need to customise the validators for the Code property instead.

              if (metadata.PropertyName == GetPropertyName(() => model.Code) && ...


              "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

              "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

              I 1 Reply Last reply
              0
              • Richard DeemingR Richard Deeming

                You register the provider in Global.asax.cs:

                ModelValidatorProviders.Providers.Clear();
                ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() );

                I'd expect to see the PropertyName set to null for the class-level validation, but I'd expect the method to be called again for each property on the class. You're currently checking whether the validators are for the CodeLength property; you need to customise the validators for the Code property instead.

                if (metadata.PropertyName == GetPropertyName(() => model.Code) && ...


                "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                I Offline
                I Offline
                indian143
                wrote on last edited by
                #10

                ModelValidatorProviders.Providers.Clear(); ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() ); I did this registration in the Global.asax.cs file, in the Application_Start() event. And even if I changed the Condition to:

                if (metadata.PropertyName == GetPropertyName(() => model.Code) && (model.CodeLength > 0))

                Still I am getting the metadata.PropertyName null always, can you please suggest me what am I missing? Just asking am I using any wrong namespace in references?, Because it was saying there is an ambiguous class names and those classes present in both the following namespaces using System.Web.ModelBinding; using System.Web.Mvc; Am I using any wrong namespace or something like that kind of error, I am not understanding why is it giving metadata.PropertyName as null in all the cases? Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

                Richard DeemingR 1 Reply Last reply
                0
                • I indian143

                  ModelValidatorProviders.Providers.Clear(); ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() ); I did this registration in the Global.asax.cs file, in the Application_Start() event. And even if I changed the Condition to:

                  if (metadata.PropertyName == GetPropertyName(() => model.Code) && (model.CodeLength > 0))

                  Still I am getting the metadata.PropertyName null always, can you please suggest me what am I missing? Just asking am I using any wrong namespace in references?, Because it was saying there is an ambiguous class names and those classes present in both the following namespaces using System.Web.ModelBinding; using System.Web.Mvc; Am I using any wrong namespace or something like that kind of error, I am not understanding why is it giving metadata.PropertyName as null in all the cases? Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

                  Richard DeemingR Offline
                  Richard DeemingR Offline
                  Richard Deeming
                  wrote on last edited by
                  #11

                  Sorry, I picked the wrong property to test against. Replace metadata.Model with metadata.Container:

                  if (metadata.Model is ServiceFunctionCategoryLKP model && metadata.PropertyName == nameof(model.Code) && model.CodeLength >= 0)


                  "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                  "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

                  I 1 Reply Last reply
                  0
                  • Richard DeemingR Richard Deeming

                    Sorry, I picked the wrong property to test against. Replace metadata.Model with metadata.Container:

                    if (metadata.Model is ServiceFunctionCategoryLKP model && metadata.PropertyName == nameof(model.Code) && model.CodeLength >= 0)


                    "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                    I Offline
                    I Offline
                    indian143
                    wrote on last edited by
                    #12

                    But still the attributes value in IEnumerable attributes coming as null when I already have one Attribute on that class and when I try to expan the attributes collection object, it shows me "Enumeration yielded no results" in the end.

                    public class LookupTable
                    {
                        public int Id { get; set; }
                    
                        \[Required(ErrorMessage = "Code is required.")\]        
                        public string Code { get; set; }
                    
                        \[Required(ErrorMessage = "Description is required.")\]
                        public string Description { get; set; }
                        public int? ForeignKeyId { get; set; }
                        public string ForeignKeyValue { get; set; }
                        public DateTime? CreatedDate {get;set;} 
                        public string CreatedBy {get;set;}       
                        public int CodeLength { get; set; }
                    }
                    

                    Do I need to add anymore Attributes to the Code Property?, what am I missing here my friend? Even after I changed my function to have Container instead of Model, still I get null at metadata.PropertyName Always metadata.PropertyName is coming as null irrespective of any changes I do at if (metadata.PropertyName == GetPropertyName(() => model.Code) && (model.CodeLength > 0)) in the below function and attributes is also coming as null.

                        protected override IEnumerable GetValidators(System.Web.Mvc.ModelMetadata metadata, ControllerContext context, IEnumerable attributes)
                        { 
                            if (metadata.Model is LookupTable)
                            {
                                LookupTable model = metadata.Container as LookupTable;                
                    
                                if (metadata.PropertyName == GetPropertyName(() => model.Code) && (model.CodeLength > 0))
                                {
                                    var newAttributes = new List(attributes);
                                    var stringLength = newAttributes.OfType().FirstOrDefault();
                                    if (stringLength != null)
                                    {
                                        newAttributes.Remove(stringLength);
                    
                                        if (model.CodeLength != 0)
                                        {
                                            newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                                            {
                                                MinimumLength = model.CodeLength,
                                                ErrorMessage = @"The field {{0}} length must be at least {model.CodeLength}."
                                            });
                                        }
                    
                                        attributes = newAttributes;
                    
                    Richard DeemingR 1 Reply Last reply
                    0
                    • I indian143

                      But still the attributes value in IEnumerable attributes coming as null when I already have one Attribute on that class and when I try to expan the attributes collection object, it shows me "Enumeration yielded no results" in the end.

                      public class LookupTable
                      {
                          public int Id { get; set; }
                      
                          \[Required(ErrorMessage = "Code is required.")\]        
                          public string Code { get; set; }
                      
                          \[Required(ErrorMessage = "Description is required.")\]
                          public string Description { get; set; }
                          public int? ForeignKeyId { get; set; }
                          public string ForeignKeyValue { get; set; }
                          public DateTime? CreatedDate {get;set;} 
                          public string CreatedBy {get;set;}       
                          public int CodeLength { get; set; }
                      }
                      

                      Do I need to add anymore Attributes to the Code Property?, what am I missing here my friend? Even after I changed my function to have Container instead of Model, still I get null at metadata.PropertyName Always metadata.PropertyName is coming as null irrespective of any changes I do at if (metadata.PropertyName == GetPropertyName(() => model.Code) && (model.CodeLength > 0)) in the below function and attributes is also coming as null.

                          protected override IEnumerable GetValidators(System.Web.Mvc.ModelMetadata metadata, ControllerContext context, IEnumerable attributes)
                          { 
                              if (metadata.Model is LookupTable)
                              {
                                  LookupTable model = metadata.Container as LookupTable;                
                      
                                  if (metadata.PropertyName == GetPropertyName(() => model.Code) && (model.CodeLength > 0))
                                  {
                                      var newAttributes = new List(attributes);
                                      var stringLength = newAttributes.OfType().FirstOrDefault();
                                      if (stringLength != null)
                                      {
                                          newAttributes.Remove(stringLength);
                      
                                          if (model.CodeLength != 0)
                                          {
                                              newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                                              {
                                                  MinimumLength = model.CodeLength,
                                                  ErrorMessage = @"The field {{0}} length must be at least {model.CodeLength}."
                                              });
                                          }
                      
                                          attributes = newAttributes;
                      
                      Richard DeemingR Offline
                      Richard DeemingR Offline
                      Richard Deeming
                      wrote on last edited by
                      #13

                      I don't know what to suggest. I've tested the code, and it works. :confused: If you're not putting a static StringLength attribute on the property, you'll need to the if (model.CodeLength != 0) block outside of the if (stringLength != null) block:

                      if (stringLength != null)
                      {
                      newAttributes.Remove(stringLength);
                      }

                      if (model.CodeLength != 0)
                      {
                      newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                      {
                      MinimumLength = model.CodeLength,
                      ErrorMessage = @"The field {{0}} length must be at least {model.CodeLength}."
                      });
                      }

                      attributes = newAttributes;

                      But if you're never seeing a value in metadata.PropertyName, then there's something else going on. I assume you have actually enabled unobtrusive validation?


                      "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                      "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

                      I 1 Reply Last reply
                      0
                      • Richard DeemingR Richard Deeming

                        I don't know what to suggest. I've tested the code, and it works. :confused: If you're not putting a static StringLength attribute on the property, you'll need to the if (model.CodeLength != 0) block outside of the if (stringLength != null) block:

                        if (stringLength != null)
                        {
                        newAttributes.Remove(stringLength);
                        }

                        if (model.CodeLength != 0)
                        {
                        newAttributes.Add(new StringLengthAttribute(model.CodeLength)
                        {
                        MinimumLength = model.CodeLength,
                        ErrorMessage = @"The field {{0}} length must be at least {model.CodeLength}."
                        });
                        }

                        attributes = newAttributes;

                        But if you're never seeing a value in metadata.PropertyName, then there's something else going on. I assume you have actually enabled unobtrusive validation?


                        "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                        I Offline
                        I Offline
                        indian143
                        wrote on last edited by
                        #14

                        No I have not done this: unobtrusive validation, can you please let me know how to do it only on one particular class and property that I want. Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

                        Richard DeemingR 1 Reply Last reply
                        0
                        • I indian143

                          No I have not done this: unobtrusive validation, can you please let me know how to do it only on one particular class and property that I want. Thanks, Abdul Aleem "There is already enough hatred in the world lets spread love, compassion and affection."

                          Richard DeemingR Offline
                          Richard DeemingR Offline
                          Richard Deeming
                          wrote on last edited by
                          #15

                          You enable unobtrusive validation for the site in the web.config file:

                          <configuration>
                          <appSettings>
                          <add key="ClientValidationEnabled" value="true"/>
                          <add key="UnobtrusiveJavaScriptEnabled" value="true"/>

                          This should be included by default, but it depends on the template you started with. Creating a MVC 3 Application with Razor and Unobtrusive JavaScript ⇒ Enabling Client-Side Validation[^] I've created a minimal sample project in VS2017 which works as expected; you can download it from OneDrive[^].


                          "These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer

                          "These people looked deep within my soul and assigned me a number based on the order in which I joined" - Homer

                          1 Reply Last reply
                          0
                          Reply
                          • Reply as topic
                          Log in to reply
                          • Oldest to Newest
                          • Newest to Oldest
                          • Most Votes


                          • Login

                          • Don't have an account? Register

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • World
                          • Users
                          • Groups