0
votes

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:

  • Set a "default" ManufacturerName value before ModelState check, i.e. "-" just to satisfy the DataAnnotation
  • Populate both ManufacturerName and ManufacturerId in my VehicleModelView - not good if your parent model has a bunch of required field you don't really need to use in a child model
  • Turn off [Required] validation for child model (not sure how?)

what do you think?

5
I'm guessing your models are Entity Framework models? This is why you shouldn't use your entity models as the models for your views. ViewModels should be specific per view. Then you can have different validation for different screens. - Simon

5 Answers

2
votes

The simplest way is to have hidden fields for the required properties you don't want to show.

1
votes

A possible solution is to add the foreign key column to the VehicleManufacturer (VehicleManufacturerId) to the VehicleModel and use that column in your view.

0
votes

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

0
votes

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

0
votes

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