.Net数据注释和模型层次结构

时间:2012-10-02 08:12:56

标签: .net asp.net-mvc-3 validation data-annotations model-validation

大家好!我很混淆实现一段代码来在asp.net mvc 3中创建工作.net数据注释,在几种情况下使用具有不同必需字段的模型(6)。 我有一个模特:

  public class OpportunityModel
{
    public Guid OpportunityId { get; set; }

    [Display(Name = "Value")]
    [RegularExpression(@"^[-+]?\d{1,10}(\.\d{0,4})?$", ErrorMessage = "Must be a number")]
    public decimal? ActualValue { get; set; }  

    [Display(Name = "Name")]
    [Required(ErrorMessage = "Name is required")]
    public string Name { get; set; } 
    public string Product { get; set; } 

    [Display(Name = "Estimated Date")]
    public DateTime? EstimateDate { get; set; }


    public bool? Sales6ixFallDown { get; set; }


    [Display(Name = "Stage")]
    public Stages Sales6ixStage { get; set; }

    public DateTime? Sales6ixDateInBoard { get; set; }

    public DateTime? Sales6ixDateInCurrentStage { get; set; }

    public DateTime? Sales6ixNextAppointmentDate { get; set; }

    [Display(Name = "Description")]
    public string Description { get; set; }

    public string Sales6ixNextAppointmentDescription { get; set; }

    public int NewColumn { get; set; }

    public Guid? CustomerId { get; set; }

    public string CustomerName { get; set; }
}

我需要的是动态改变其中所需的封地的可能性。经过一些谷歌搜索,这是不可能的,并开始使用模型继承。我的意思是:我有一个像这样的基础模型:

  public class BaseOpportunityModel
{
    public Guid OpportunityId { get; set; }

    public virtual decimal? ActualValue { get; set; }  
    public virtual string Name { get; set; }  

    public string Product { get; set; } 

    public DateTime? EstimateDate { get; set; }

    public bool? Sales6ixFallDown { get; set; }


    [Display(Name = "Stage")]
    public Stages Sales6ixStage { get; set; }

    public DateTime? Sales6ixDateInBoard { get; set; }

    public DateTime? Sales6ixDateInCurrentStage { get; set; }

    public DateTime? Sales6ixNextAppointmentDate { get; set; }

    [Display(Name = "Description")]
    public string Description { get; set; }

    public string Sales6ixNextAppointmentDescription { get; set; }

    public int NewColumn { get; set; }

    public Guid? CustomerId { get; set; }

    public string CustomerName { get; set; }
}

其中虚拟属性是可能是必填字段的属性。然后我从这个基地得到了几个派生模型:

  public class OpportunityModel0: BaseOpportunityModel
{
    [Display(Name = "Value")]
    [Required(ErrorMessage = "Name is required")]
    [RegularExpression(@"^[-+]?\d{1,10}(\.\d{0,4})?$", ErrorMessage = "Must be a number")]
    public override decimal? ActualValue { get; set; }  

    [Display(Name = "Name")]
    [Required(ErrorMessage = "Name is required")]
 public override string Name { get; set; }  

}

然后我就可以在View和Controller基础模型BaseOpportunityModel中使用了。但我遇到了以下问题:

  • 验证使用BaseOpportunityModel中的注释属性并忽略派生模型中的属性。

我错了什么?有人能引导我朝着正确的方向前进吗?还是帮我解决这个问题?提前谢谢。

2 个答案:

答案 0 :(得分:1)

此提示应在mvc 3中适用于THIS。可能有问题的一件事是你的后期行动。您应该在后期操作中将继承的模型指定为param。

public ActionResult MyPostAction(OpportunityModel0 model)

如果基本模型是参数,则验证将无效。

答案 1 :(得分:1)

我通过使用自定义RequiredIfValidator来解决模型的不同重新验证问题。所以现在我只有一个模型和一个视图。这是代码,可能是某些人发现它很有用:

RequiredIfAttribute:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Infrastructure.Extensions
{
    public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
    {
        private RequiredAttribute _innerAttribute = new RequiredAttribute();

        public string DependentProperty { get; set; }
        public object TargetValue { get; set; }

        public RequiredIfAttribute(string dependentProperty, object targetValue)
        {
            this.DependentProperty = dependentProperty;
            this.TargetValue = targetValue;
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            // get a reference to the property this validation depends upon
            var containerType = validationContext.ObjectInstance.GetType();
            var field = containerType.GetProperty(this.DependentProperty);

            if (field != null)
            {
                // get the value of the dependent property
                var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);

                // compare the value against the target value
                if ((dependentvalue == null && this.TargetValue == null) ||
                    (dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
                {
                    // match => means we should try validating this field
                    if (!_innerAttribute.IsValid(value))
                        // validation failed - return an error
                        return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
                }
            }

            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule()
            {
                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                ValidationType = "requiredif",
            };

            string depProp = BuildDependentPropertyId(metadata, context as ViewContext);

            // find the value on the control we depend on;
            // if it's a bool, format it javascript style 
            // (the default is True or False!)
            string targetValue = (this.TargetValue ?? "").ToString();
            if (this.TargetValue.GetType() == typeof(bool))
                targetValue = targetValue.ToLower();

            rule.ValidationParameters.Add("dependentproperty", depProp);
            rule.ValidationParameters.Add("targetvalue", targetValue);

            yield return rule;
        }

        private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
        {
            // build the ID of the property
            string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
            // unfortunately this will have the name of the current field appended to the beginning,
            // because the TemplateInfo's context has had this fieldname appended to it. Instead, we
            // want to get the context as though it was one level higher (i.e. outside the current property,
            // which is the containing object (our Person), and hence the same level as the dependent property.
            var thisField = metadata.PropertyName + "_";
            if (depProp.StartsWith(thisField))
                // strip it off again
                depProp = depProp.Substring(thisField.Length);
            return depProp;
        }
    }
}



RequiredIfValidator

namespace Infrastructure.Extensions
{
    public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
    {
        public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
            : base(metadata, context, attribute)
        {
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            return base.GetClientValidationRules();
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            // get a reference to the property this validation depends upon
            var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);

            if (field != null)
            {
                // get the value of the dependent property
                var value = field.GetValue(container, null);

                // compare the value against the target value
                if ((value == null && Attribute.TargetValue == null) ||
                    (value.Equals(Attribute.TargetValue)))
                {
                    // match => means we should try validating this field
                    if (!Attribute.IsValid(Metadata.Model))
                        // validation failed - return an error
                        yield return new ModelValidationResult { Message = ErrorMessage };
                }
            }
        }
    }
}



客户端验证

/// <reference path="jquery-1.4.4-vsdoc.js" />
    /// <reference path="jquery.validate.unobtrusive.js" />

    $.validator.addMethod('requiredif',
        function (value, element, parameters) {
            var id = '#' + parameters['dependentproperty'];

            // get the target value (as a string, 
            // as that's what actual value will be)
            var targetvalue = parameters['targetvalue'];
            targetvalue =
              (targetvalue == null ? '' : targetvalue).toString();

            // get the actual value of the target control
            // note - this probably needs to cater for more 
            // control types, e.g. radios
            var control = $(id);
            var controltype = control.attr('type');
            var actualvalue =
                controltype === 'checkbox' ?
                control.is(":checked").toString() :
            //control.attr('checked').toString() :
                control.val();

            actualvalue = actualvalue.toLocaleLowerCase();

            // if the condition is true, reuse the existing 
            // required field validator functionality
            if (targetvalue === actualvalue)
                return $.validator.methods.required.call(
                  this, value, element, parameters);

            return true;
        }
    );

    $.validator.unobtrusive.adapters.add(
        'requiredif',
        ['dependentproperty', 'targetvalue'], 
        function (options) {
            options.rules['requiredif'] = {
                dependentproperty: options.params['dependentproperty'],
                targetvalue: options.params['targetvalue']
            };
            options.messages['requiredif'] = options.message;
        });