Reputation: 10147
I would like to "turn off" the Required field validation on a certain property of the parent model (VehicleManufacturer), when saving a child model (VehicleModel), i.e.:
public class VehicleManufacturer
{
public virtual Guid Id { get; set; }
[Required]
[StringLength(50, MinimumLength = 1)]
public virtual string Name { get; set; }
}
public class VehicleModel
{
public virtual Guid Id { get; set; }
[Required]
[StringLength(50, MinimumLength = 1)]
public virtual string Name { get; set; }
public virtual VehicleManufacturer Manufacturer { get; set; }
}
So, when I'm saving a new model, all I care about is it's Name and ManufacturerID, which would be selected from a drop-down list, however, because the ManufacturerName is marked [Required] in its entity, it invalidates my ModelState when saving a new VehicleModel, because ManufacturerName is null :(
I would like to know what is the best approach to this and how to do it. I can think of a few solutions but none seem to be the right way:
what do you think?
Upvotes: 0
Views: 2668
Reputation: 3596
Ok. I looked at this and realized my method was wasteful.
How does this look?
I created an Annotation called ExtendedValidationRequired.
It has a static value that turns on conditional checking for extended values or not.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class ExtendedValidationRequiredAttribute : RequiredAttribute
{
// Summary:
// Initializes a new instance of the System.ComponentModel.DataAnnotations.RequiredAttribute
// class.
public ExtendedValidationRequiredAttribute()
{
}
// Summary:
// Checks that the value of the required data field is not empty.
//
// Parameters:
// value:
// The data field value to validate.
//
// Returns:
// true if validation is successful; otherwise, false.
//
// Exceptions:
// System.ComponentModel.DataAnnotations.ValidationException:
// The data field value was null.
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (ExtendedValidation.IsExtendedValidationEnabled(validationContext.ObjectType))
{
return base.IsValid(value,validationContext);
}
else
{
return ValidationResult.Success;
}
}
}
I then mark up my Sometimes required (eg when im directly editing that class) fields with [ExtendedValidationRequired(typeof(MyClassName))]
this works for other types of validators as well.
I actually went ahead and created a set of 'Sometimes On' validators and moved my setting to a separate class: public static class ExtendedValidation { private static Dictionary extendedValidationExemptions = new Dictionary();
/// <summary>
/// Disable extended validation for a specific type
/// </summary>
/// <param name="type"></param>
public static void DisableExtendedValidation(Type type)
{
extendedValidationExemptions[type] = true;
}
/// <summary>
/// Clear any EV exemptions
/// </summary>
public static void Reset()
{
extendedValidationExemptions.Clear();
}
/// <summary>
/// Check if a class should perform extended validation
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static bool IsExtendedValidationEnabled(Type type)
{
if (extendedValidationExemptions.ContainsKey(type))
{
return false;
}
else
{
return true;
}
}
}
}
now i just turn ExtendedValidation off when editing children.
E.G: When editing a Child, Can DisableExtendedValidation(typeof(Parent)) and not get in the way.
Edit: Hrm, I realize this doesnt quite work. -- Can I determine what class I am looking at inside of a validationProperty? I guess I could always pass the parent property in but that is a PITA
Upvotes: 0
Reputation: 3596
What I ended up doing is subclassing the parent model with something like ParentWithExtendedValidation : Parent
In ParentWithExtendedValidation all of the various [Required] Fields were tagged.
When I wanted to specifically edit the parent, I used type ParentWithExtendedValidation -- since it is a subclass of Parent, once it passes model validation, you can cast it back to parent and pass it to your DAL with no issues.
When you are using it in a relationship e.g editing a child that references this, you can use the regular Parent class.
Hope this helps
EG I have a class Person -- with all of my normal virtual properties, and only the ID is required (to make validation that selects on person->ID still work right)
And a Class PersonEV
public class PersonWithExtendedValidation : Person
{
[Required]
public override string FullName { get; set; }
[Required]
public override string FirstName { get; set; }
[Required]
public override string LastName { get; set; }
[Required]
public override string DomainName { get; set; }
[Required]
public override string WorkPhone { get; set; }
[Required]
public override string CellPhone { get; set; }
[Required]
public override string Email { get; set; }
}
Then in your View/Controller work with e.g PersonEV for the model and argument.
Once you check for ModelState.IsValid , cast back to (Person) and pass to whatever repository or etc as normal
Upvotes: 0
Reputation: 1985
A possible solution is to add the foreign key column to the VehicleManufacturer (VehicleManufacturerId) to the VehicleModel and use that column in your view.
Upvotes: 1
Reputation: 5430
The IValidatableObject
interface is for custom model validation.
For example:
public class VehicleModel : IValidatableObject
{
public virtual Guid Id { get; set; }
[StringLength(50, MinimumLength = 1)]
public virtual string Name { get; set; }
public virtual VehicleManufacturer Manufacturer { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if(string.IsNullOrWhiteSpace(Name))
{
yield return new ValidationResult("Name is required")
}
}
}
Then in your controller call ModelState.IsValid
Upvotes: 0
Reputation: 174299
The simplest way is to have hidden fields for the required properties you don't want to show.
Upvotes: 2