Search code examples
c#asp.net-mvccustom-attributesasp.net-mvc-viewmodel

Inherited = false doesn't work for attribute of field in viewModel


I made test attribute

    [AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field, Inherited = false)]
    public class NonInheritedRequiredAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value == null)
                return new ValidationResult((validationContext.DisplayName));
            return null;
        }
    }

And viewModels

    public class ViewModelA
    {
        [NonInheritedRequired]
        public virtual string Property{ get; set; }
    }

    public class ViewModelB : ViewModelA
    {
        public override string Property{ get; set; }
    }

Action method

        [HttpPost]
        public ActionResult Index(ViewModelB viewModel)
        {
            if (ModelState.IsValid)
            {
                return View("OtherView");
            }

            return View(viewModel);
        }

ModelState always is not valid. On validating it uses validation of NonInheritedRequired despite it has Inherited = false. How can I solve this problem?

Thanks.


Solution

  • The Inherited property isn't respected in MVC.

    possible solutions:

    1. Implement a custom ModelValidatorProvider which will filter out non inheritable validation attributes.

    2. Filter out non inheritable attributes by providing a custom TypeDescriptionProvider

    3. Use MetadataType attribute in your models. This is the easiest of all.

    Sample

    [MetadataType(typeof(ModelAMetadata))]
    public class ModelA
    {
        public virtual string Property { get; set; }
    }
    
    public class ModelAMetadata
    {
        [Required]
        public string Property { get; set; }
    }
    
    [MetadataType(typeof(ModelBMetadata))]
    public class ModelB : ModelA
    {
        public override string Property { get; set; }
    }
    
    public class ModelBMetadata
    {
        //notice that there is no Required attribute here
        public string Property { get; set; }
    }
    

    Most elegant solution is #1, but I suggest to redesign the Models instead.