Want to change the Validation attribute at runtime ASP.Net MVC
-
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."
-
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."
-
"Baked in" at compile time, apparently. Might try overriding the properties in question with a derived class for each of the "different conditions".
"(I) am amazed to see myself here rather than there ... now rather than then". ― Blaise Pascal
-
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."
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
-
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
-
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."
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 theStringLength
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
-
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 theStringLength
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
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."
-
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 theStringLength
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
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."
-
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."
You register the provider in
Global.asax.cs
:ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() );I'd expect to see the
PropertyName
set tonull
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 theCodeLength
property; you need to customise the validators for theCode
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
-
You register the provider in
Global.asax.cs
:ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add( new CustomValidatorProvider() );I'd expect to see the
PropertyName
set tonull
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 theCodeLength
property; you need to customise the validators for theCode
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
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."
-
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."
Sorry, I picked the wrong property to test against. Replace
metadata.Model
withmetadata.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
-
Sorry, I picked the wrong property to test against. Replace
metadata.Model
withmetadata.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
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;
-
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;
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 theif (model.CodeLength != 0)
block outside of theif (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 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 theif (model.CodeLength != 0)
block outside of theif (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
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."
-
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."
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