Web Api ModelState validation is ignoring the DisplayAttribute - validation

Given a model with these data annotations:
public class Example
{
[Required]
[Display(Name = "Activity response")]
public string ActivityResponse { get; set; }
}
I would expect the model state error message to be "The Activity response field is required." Instead it is "The ActivityResponse field is required."

Hooray! The codeplex issue reports that this bug will be fixed in Web API v5.1 Preview.

Had the same problem and I made a workaround for it.
I know it is not perfect.
For every dataannotation attribute create a new class
public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
validationContext.DisplayName = ModelMetadataProviders
.Current
.GetMetadataForProperty(null, validationContext.ObjectType, validationContext.DisplayName)
.DisplayName;
return base.IsValid(value, validationContext);
}
}
public class StringLengthAttribute : System.ComponentModel.DataAnnotations.StringLengthAttribute
{
public StringLengthAttribute(int maximumLength)
: base(maximumLength)
{ }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
validationContext.DisplayName = ModelMetadataProviders
.Current
.GetMetadataForProperty(null, validationContext.ObjectType, validationContext.DisplayName)
.DisplayName;
return base.IsValid(value, validationContext);
}
}
etc....

Related

How to access property of parent in validation attribute

In my code below I want to check with AttributeValidation if a field is given dependent on a property of its parent element. The comment in the class
RequiredIfParentState1
describes my question best.
public class ChildModel()
{
[RequiredIfParentState1]
public string ImRequired { get; set; }
}
public class ParentViewModel()
{
public int state { get; set; }
public ChildModel child = new ChildModel();
}
public class RequiredIfParentState1: ValidationAttribute, IClientModelValidator
{
RequiredIfParentState1()
{
}
void AddValidation(ClientModelValidationContext context)
{
}
protected override ValidationResult IsValid(object i_value, ValidationContext i_context)
{
var element = i_context.ObjectInstance;
if(i_value == null && //what do i have to put here to check if the state is 1?)
{
return new ValidationResult($"Field is Required in state 1.");
}
return ValidationResult.Success;
}
}
I feel this is the wrong approach.
An object being in a valid state is one thing (required fields and type checking), but handling business logic is a separate concern.
You could write a validation service, that examines the model in detail, checking business logic concerns, and build up a list of errors.
Where errors are found you can return these in your response.

.net core custom model binding

I have a model such as
public class MyModel
{
public MyObject myObject {get;set;}
}
public class MyObject
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
With out using a custom model binder everything works great. I am trying to implement a model binder and not getting anywhere -- the resources that I have come from are
https://www.youtube.com/watch?v=qDRORgoZxZU (returns null model to the controller)
http://intellitect.com/custom-model-binding-in-asp-net-core-1-0/ (controller dies on the constructor)
http://hotzblog.com/asp-net-vnext-defaultmodelbinder-and-automatic-viewmodel-string-trim/ (can not even find MutableObjectModelBinder in the .net-core namespace)
Ideally what I want is to track which properties where set by the ModelBinder.
public class MyObject
{
public string FirstName {get;set;}
public string LastName {get;set;}
public List<String> ModifiedProperties {get;set;}
}
when the object is created by the ModelBinder for each property that is being set it adds it to the ModifiedProperties list.
This is solution. You need to implement IModelBinderProvider and IModelBinder
public class EntityFrameworkModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
//We only want to invoke the CustomeBinder on IBaseEntity classes
if (context.Metadata.ContainerType != null && context.Metadata.ContainerType.GetInterfaces().Contains(typeof(SurgeOne.Core.IBaseEntity)))
{
//We only create the custom binder on value types. E.g. string, guid, etc
if (context.Metadata.ModelType.GetTypeInfo().IsValueType ||
context.Metadata.ModelType == typeof(System.String))
{
return new EntityFrameworkModelBinder();
}
}
return null;
}
}
And IModelBinder
public class EntityFrameworkModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
//Get the value
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult == ValueProviderResult.None)
{
// no entry
return Task.CompletedTask;
}
//Set the value -- not sure what this does
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
//Set the value -- this has to match the property type.
System.ComponentModel.TypeConverter typeConverter = System.ComponentModel.TypeDescriptor.GetConverter(bindingContext.ModelType);
object propValue = typeConverter.ConvertFromString(valueProviderResult.FirstValue);
bindingContext.Result = ModelBindingResult.Success(propValue);
//Code to track changes.
return Task.CompletedTask;
} //BindModelAsync
}

Access property values with an attribute

How do I access the value of a property inside an attribute class. I'm writing a custom validation attribute that needs to check the value of the property against a regular expression. The
For Instance:
public class MyAttribute
{
public MyAttribute (){}
//now this is where i want to use the value of the property using the attribute. The attribute can be use in different classed
public string DoSomething()
{
//do something with the property value
}
}
Public class MyClass
{
[MyAttribute]
public string Name {get; set;}
}
If you just want to use a regular expression validation attribute, then you can inherit from RegularExpressionAttribute, see https://stackoverflow.com/a/8431253/486434 for an example of how to do that.
However, if you want to do something more complex and access the value you can inherit from ValidationAttribute and override the 2 virtual methods IsValid. E.g.:
public class MyAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// Do your own custom validation logic here
return base.IsValid(value);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
return base.IsValid(value, validationContext);
}
}

Implementing Unique Contraint with ValidateEntity gives "The given key was not present in the dictionary" error

While in search of trying to implement unique key validations for my db using EF CodeFirst/Mvc3 I came upon this post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx which gave an example on how to do it by using IValidateObject for my object model:
public class Category : IValidatableObject
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var testContext = (TestContext)validationContext.Items["Context"];
if (testContext.Categories.Any(
c => c.CategoryName == CategoryName && c.CategoryID != CategoryID))
{
yield return new ValidationResult("A category with the same name already exists!", new[] { "CategoryName" });
}
yield break;
}
}
and overriding DbEntityValidationResult ValidateEntity:
public class TestContext : DbContext
{
public DbSet<Test.Models.Category> Categories { get; set; }
protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
}
And the action on the controller
[HttpPost]
public ActionResult Create(Category category)
{
if (ModelState.IsValid) {
categoryRepository.InsertOrUpdate(category);
categoryRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
But I get the error: "The given key was not present in the dictionary." for the line
var testContext = (TestContext)validationContext.Items["Context"];
It seems like Validate on the object is getting called which accesses "Context" before its set in the override ValidateEntity code.
At first I thought it could have been ModelState.Isvalid triggering validate too early but it wasn't.
Anyone know what I'm missing here or what I'm doing wrong? Thanks in advance.
Model.IsValid definitely triggers it too early and perhaps something else. IValidatableObject is global interface used by both MVC and EF but your method in DbContext is called only when you call SaveChanges on the context so any usage of IValidatableObject prior to calling SaveChanges will result in the exception. You must use another approach if you want to validate your entity this way. For example store context in HttpContext.Items - you can create custom action filter and instantiate and store the context before the operation call and dispose it after operation call - hopefully it will cover all problems.
I was facing the same problem... Then after a lot of Googling I finally found this:
Exercise 3: Using IValidatableObject Custom Validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
MusicStoreEntities storeDB = new MusicStoreEntities();
if (storeDB.Albums.Any(
a => a.Title.Trim().ToUpper() == this.Title.Trim().ToUpper() &&
a.ArtistId == (int)this.ArtistId))
{
yield return new ValidationResult("Existing Album", new string[] { "Title" });
}
}
As you see in their example, they instantiate a new Context and as such there's no need for validationContext.Items["Context"];. Doing so we won't get this error anymore.

ASP.NET MVC : DataAnnotation validation execution order

I'm having some trouble understanding validation logic behing DataAnnotation validation :
With the following model :
[AlwaysInvalid]
public class TestModel
{
[Required]
public string Test { get; set; }
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class AlwaysInvalidAttribute : ValidationAttribute
{
private readonly object typeId = new object();
public AlwaysInvalidAttribute() : base("Fail !") {}
public override object TypeId { get { return this.typeId; } }
public override bool IsValid(object value)
{
return false;
}
}
The AlwaysInvalidAttribute error message gets displayed only if the Required attribute is valid : I can't get both messages at the same time. Anyone got an idea why ? I think it's an issue with DefaultModelBinder, but still haven't found where, or why.
Class-level validators only run if all the property-level validators were successful. This behavior is coded up in the ModelValidator class.

Resources