I'm trying to resolve a problem I found while trying to do a custom validation attribute on my daughter's dog store using blazor.
Basically I have a property as follow:
[Test]
public int? PetAge { get; set; }
As you can see, I'm going to create a custom attribute called "Test", so I created a new class as follow:
public class Test : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
return new ValidationResult("niet! niet!");
}
}
and my understanding is that I can return a new ValidationResult with a string message or if everything goes fine, I can return ValidationResult.Success. In my example, it should always return a problem, as we did not provide any other logic than always returning a validationresult.
When running this code, it simply does not return anything, no error displayed on the form. Now what I did notice is that if I use the other version of the IsValid method, it works, the only problem is that I would like to send a custom message back and not just a true/false result. The one working is as follow:
public override bool IsValid(object? value)
{
return false;
}
But in that case, I have to provide the error message in the parameter like this:
[Test(ErrorMessage("this is not ok!")]
public int? PetAge { get; set; }
As these parameters must be constants, it was an awesome idea not only to be able to display a custom-built message as an error and/or have access to the context, etc...
am I doing something wrong on my Blazor app? any help will be appreciated!
I noticed this too, try the following
public class Test : ValidationAttribute
{
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
return new ValidationResult("niet! niet!", new[] { validationContext.MemberName });
}
}
Related
My route is defined as http:/.../home/index/1 which is (controller/action/id).
ViewModel is as below..
public class TestVM
{
[CustomValidation]
public string name{get; set;}
}
public class CustomValidation : ValidationAttribute
{
protected override ValidationResult? IsValid (object? value, validationContext)
{
var vm = (TestVM)validationContext.ObjectInstance;
//how to get the route value here?
}
}
To validate Name property, I need to have value of Id. To access route value in the IsValid method, I defined one more property Id in the TestVM.
Is there a way to access the Id in the IsValid without defining in the TestVM?
Is there a way to access the Id in the IsValid without defining in the
TestVM?
Seems you are trying to access parameter which has been passed to controller route so that, you can get those values inside your CustomValidation class
Well, using IHttpContextAccessor we can get all route value from which are available within HttpContext.Request. Thus, you can access property name of member of TestVM class.
Access Route Value Inside ValidationAttribute:
You can implement in following way:
public class CustomValidator : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var vm = context.ObjectInstance as TestVM;
var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>()
var getIdFromRouteValue = httpContextAccessor.HttpContext.Request.Query["Id"];
var getNameFromRouteValue = httpContextAccessor.HttpContext.Request.Query["name"];
vm.Id = getIdFromRouteValue;
vm.name = getNameFromRouteValue;
return ValidationResult.Success;
}
}
Program.cs:
IHttpContextAccessor requires to register AddHttpContextAccessor() service on your program.cs file. You can do as following:
builder.Services.AddHttpContextAccessor();
Note: Please make sure you have followed the correct order. You can get more details on our official document here.
Output:
I am using .NET Core and have the following code in a view model:
public bool IsLocalArrangement { get; set; }
[RequiredIfLocalArrangements]
public string LocalArrangementDetail { get; set; }
For this annotation to work, I created a new class, and have the following code:
using System.ComponentModel.DataAnnotations;
using MyProject.ViewModels;
namespace MyProject.Validation
{
public class RequiredIfLocalArrangements : ValidationAttribute
{
protected override ValidationResult IsValid(object value,
ValidationContext validationContext)
{
var vm = (ArrangementsViewModel)validationContext.ObjectInstance;
return string.IsNullOrEmpty((string)value) && vm.IsLocalArrangement ?
new ValidationResult("Please provide details for the local arrangement(s)")
: ValidationResult.Success;
}
}
}
However this scenario is coming up a lot, and I'm now replicating all this code to new classes (RequiredIfOwnsVehicle, etc.), where the only variables I need to change are the view model name, the boolean property within the view model that it depends on, and the validation result message.
How can I make this generic?
EDIT:
In trying to explore the foolproof project from github I run into this issue with System.Web.Mvc
I've looked at a number of questions on creating custom data annotation classe for model validation and so far I can't extrapolate an answer from any of them as they all vary quite wildly in responses.
I am trying to run a method that returns a boolean on what's been entered into the textbox for that property but I'm not sure how to get at the property contents to run the method. Basically this is a check to see if this username already exists. Here is what I've tried but as you can see, I don't know how to get at the string entered into the field to run the method on.
public class Username : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ADSI adsi = new ADSI();
if (adsi.UserExists(//here's where the text entered should go))
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
I figured it out. The value object is the text that the user has entered into the field.
public class Username : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ADSI adsi = new ADSI();
if (adsi.UserExists(value.ToString()))
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
Obviously the method I use for checking if a username exists is something I wrote so you'll need to refer to documentation on how to do that.
Usage:
[Username(ErrorMessage = "Username already exists.")]
public string Username {get; set;}
In my view I have a check box and a text box if the check box is checked then I require the text box to be filled with some text. To do this I call
ModelState.AddModelError("item", "Please enter some text.");
only if the checkbox returns true and the text box isempty
when my page re-displays I receive the proper message where I have
#Html.ValidationMessageFor(model => model.item)
but I would like the text to go away after a use types something in the text box, without the user having to hit submit like it does with data annotation. How can I fix this?
I'm using c# Asp.net 4 with entity framework 5
ModelState.AddModelError is server-side validation, so the error message will not go away until you post to the server.
If you want the functionality you describe, you can define a custom validation attribute and apply it both client side and server-side. For example, you can define a "RequiredIf" custom validation attribute, which would make a field required if a certain other condition is met (in this case, if another property is true):
public class RequiredIfAttribute : RequiredAttribute
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue.ToString() == DesiredValue.ToString())
{
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
Register it in your global.asax:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter);
Then you can use it like this:
public class YourModel {
// This is the property tied to your checkbox
public bool YourBooleanProperty { get; set; }
[RequiredIf("YourBooleanProperty", true)]
public string Item { get; set; }
}
You could also leverage the JQuery Validate plugin to perform the same conditional validation client-side.
Try with jquery, attach an eventListener to the field and remove the class CSS that MVC added to the field and hide the validation label
Many forms in my project have a part number input. Currently in the controller I often times test the part number to see if the part exists or user has access or if the part is obsolete, then based on the condition I reload the view passing a message through a string parameter then viewbag for display to show why the form submit failed. I'm trying to clean this up and instead use model property validation.
I have a few working well however one of the validations I want to test is if the part is obsolete and has a suggested alternate part number to use. Based on the property value (part number) I have a service layer method that will return a bool of if the part is obsolete and another one that will return the suggested use part number. If possible I'd like to trigger the validation on the bool check, then pass that alternate part number into the validation message that gets generated.
Here is the validation code in it's current form:
public class PartAlternateValidation : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
GP10Service gp10svc = new GP10Service();
//string altPart = gp10svc.GetPartAlternate(value.ToString());
return gp10svc.CheckPartAlternate(value.ToString());
}
}
Did quite a bit of searching around but couldn't find anything that specifically discussed this. Thinking maybe I could have an out string parameter with the bool in IsValid, but not sure how to then pass that to the message (or call FormatErrorMessage method from the IsValid method?). Thinking maybe there is a way using ModelState.AddModelError, however I believe the key on these is tied to the property, correct? So I'm fuzzy on how I could detect when the property fails a particular validation and use the property value as a variable in generating the message that way.
Probably something simple, has been a good excuse to do more research and I will continue reading but any suggestions or tips would be welcome.
Thanks.
Please see below tip helps..
Model:
namespace Mvc4Test.Models
{
public class Part
{
[AlternateValidation(Suggetion = "Please Use 123 (This is a Suggestion)")]
public string PartNumber { get; set; }
}
public class AlternateValidation : ValidationAttribute
{
public string Suggetion { get; set; }
protected override ValidationResult IsValid(object value,ValidationContext validationContext)
{
if (value != null)
{
if (value.ToString() == "123")
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(Suggetion);
}
}else
return new ValidationResult("Value is Null");
}
}
}
View:
#Html.EditorFor(model => model.PartNumber)
#Html.ValidationMessageFor(model => model.PartNumber)