使用数据注释的条件必需属性


68

我有一个这样的课:

public class Document
{
   public int DocumentType{get;set;}

   [Required]
   public string Name{get;set;}

   [Required]
   public string Name2{get;set;}
}

现在,如果我[Required]NameName2属性上添加数据注释,则一切正常,如果NameName2为空,则验证将引发错误。

但我希望Name仅在DocumentType等于1Name2时才需要字段,而在等于2时才需要字段DocumentType

public class Document
{
   public int DocumentType{get;set;}

   [Required(Expression<Func<object, bool>>)]
   public string Name{get;set;}

   [Required(Expression<Func<object, bool>>)]
   public string Name2{get;set;}
}

但我知道我做不到,这会导致错误。我该怎么办?


实现您的模型这一ivalidatableobject并运行该模型自定义代码- msdn.microsoft.com/en-us/library/...
克里斯McKelt

Answers:


8

开箱即用,我认为这仍然是不可能的。

但是我发现了这篇关于Mvc.ValidationToolkit的有前途的文章 (也是在这里,不幸的是,只是alpha版本,但是您可能还可以从此代码中提取所需的方法并自己集成),它包含了不错的声音属性RequiredIf似乎与您的原因完全匹配:

  • 您可以从链接的zip下载项目并进行构建
  • 从您的生成文件夹中获取生成的dll,并在您正在使用的项目中引用它
  • 不幸的是,这似乎也需要引用MVC(最简单的方法是在VS或中启动MVC项目install-package Microsoft.AspNet.Mvc)。
  • 在您要使用它的文件中,添加 using Mvc.ValidationToolkit;
  • 那么您就可以编写诸如[RequiredIf("DocumentType", 2)]或的内容[RequiredIf("DocumentType", 1)],因此只要不提供namename2,只要DocumentType不等于1或2 ,对象就有效

100

必需如果验证属性

RequiredIfAttribute当不同的属性具有特定的值(您需要什么)或其他属性没有任何属性时,我写了一个需要特定属性值的属性特定值之外的值的。

以下代码可能会有所帮助:

/// <summary>
/// Provides conditional validation based on related property value.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
    #region Properties

    /// <summary>
    /// Gets or sets the other property name that will be used during validation.
    /// </summary>
    /// <value>
    /// The other property name.
    /// </value>
    public string OtherProperty { get; private set; }

    /// <summary>
    /// Gets or sets the display name of the other property.
    /// </summary>
    /// <value>
    /// The display name of the other property.
    /// </value>
    public string OtherPropertyDisplayName { get; set; }

    /// <summary>
    /// Gets or sets the other property value that will be relevant for validation.
    /// </summary>
    /// <value>
    /// The other property value.
    /// </value>
    public object OtherPropertyValue { get; private set; }

    /// <summary>
    /// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is <c>false</c>).
    /// </summary>
    /// <value>
    ///   <c>true</c> if other property's value validation should be inverted; otherwise, <c>false</c>.
    /// </value>
    /// <remarks>
    /// How this works
    /// - true: validated property is required when other property doesn't equal provided value
    /// - false: validated property is required when other property matches provided value
    /// </remarks>
    public bool IsInverted { get; set; }

    /// <summary>
    /// Gets a value that indicates whether the attribute requires validation context.
    /// </summary>
    /// <returns><c>true</c> if the attribute requires validation context; otherwise, <c>false</c>.</returns>
    public override bool RequiresValidationContext
    {
        get { return true; }
    }

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="RequiredIfAttribute"/> class.
    /// </summary>
    /// <param name="otherProperty">The other property.</param>
    /// <param name="otherPropertyValue">The other property value.</param>
    public RequiredIfAttribute(string otherProperty, object otherPropertyValue)
        : base("'{0}' is required because '{1}' has a value {3}'{2}'.")
    {
        this.OtherProperty = otherProperty;
        this.OtherPropertyValue = otherPropertyValue;
        this.IsInverted = false;
    }

    #endregion

    /// <summary>
    /// Applies formatting to an error message, based on the data field where the error occurred.
    /// </summary>
    /// <param name="name">The name to include in the formatted message.</param>
    /// <returns>
    /// An instance of the formatted error message.
    /// </returns>
    public override string FormatErrorMessage(string name)
    {
        return string.Format(
            CultureInfo.CurrentCulture,
            base.ErrorMessageString,
            name,
            this.OtherPropertyDisplayName ?? this.OtherProperty,
            this.OtherPropertyValue,
            this.IsInverted ? "other than " : "of ");
    }

    /// <summary>
    /// Validates the specified value with respect to the current validation attribute.
    /// </summary>
    /// <param name="value">The value to validate.</param>
    /// <param name="validationContext">The context information about the validation operation.</param>
    /// <returns>
    /// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult" /> class.
    /// </returns>
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (validationContext == null)
        {
            throw new ArgumentNullException("validationContext");
        }

        PropertyInfo otherProperty = validationContext.ObjectType.GetProperty(this.OtherProperty);
        if (otherProperty == null)
        {
            return new ValidationResult(
                string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", this.OtherProperty));
        }

        object otherValue = otherProperty.GetValue(validationContext.ObjectInstance);

        // check if this value is actually required and validate it
        if (!this.IsInverted && object.Equals(otherValue, this.OtherPropertyValue) ||
            this.IsInverted && !object.Equals(otherValue, this.OtherPropertyValue))
        {
            if (value == null)
            {
                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            }

            // additional check for strings so they're not empty
            string val = value as string;
            if (val != null && val.Trim().Length == 0)
            {
                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            }
        }

        return ValidationResult.Success;
    }
}

2
做得好,喜欢它,将添加一个not参数,仅检查它是否不是东西,而是仅当它是;)-谢谢
Pakk 2015年

您能否提供如何使用此属性?我无法在ViewModel中弄清楚如何获取数据并将其传递给属性。
菲利普

2
@Pakk实际上已经是该代码的一部分。看到IsInverted充当倒置房屋的财产,如果...
Robert Koritnik

1
@Philippe:这是一个通常直接由框架运行的属性,因此您实际上不需要传递任何数据。您只需在数据模型POCO上声明性地设置它即可。一个典型的例子就是网上商店。在创建发票期间,系统会询问用户这是个人购买还是公司购买。如果他们检查公司,则还需要其他字段。此类数据模型属性(与那些字段相关)将在其[RequiredIf('IsCompany', true)]上具有此属性,该属性IsCompany: bool通常绑定到复选框。希望这可以帮助。
罗伯特·科里特尼克'16

3
做得好,一个问题:是否可以对此添加不打扰的验证?
Sirwan Afifi

33

使用数据注释的条件必需属性

 [RequiredIf(dependent Property name, dependent Property value)]

e.g. 


 [RequiredIf("Country", "Ethiopia")]
 public string POBox{get;set;}
 // POBox is required in Ethiopia
 public string Country{get;set;}

 [RequiredIf("destination", "US")]
 public string State{get;set;}
 // State is required in US

 public string destination{get;set;}



public class RequiredIfAttribute : ValidationAttribute
{
    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)
    {
        var field = validationContext.ObjectType.GetProperty(_dependentProperty);
        if (field != null)
        {
            var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
            if ((dependentValue == null && _targetValue == null) || (dependentValue.Equals(_targetValue)))
            {
                if (!_innerAttribute.IsValid(value))
                {
                    string name = validationContext.DisplayName;
                    return new ValidationResult(ErrorMessage=name + " Is required.");
                }
            }
            return ValidationResult.Success;
        }
        else
        {
            return new ValidationResult(FormatErrorMessage(_dependentProperty));
        }
    }
}


1

查看MVC万无一失验证。如果我没记错的话它在诸如RequiredIf(依赖属性,依赖值)之类的模型中具有数据注释。您可以从以下位置下载万无一失:
Visual Studio(2017)->工具-> Nuget软件包管理器->管理解决方案的Nuget软件包。除jquery文件外,还参考mvcfoolproof.unobtrusive.min.js。


1

我一直使用System.ComponentModel.DataAnnotations中的已实现IValidatableObject;

下面的例子

  public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (this.SendInAppNotification)
            {
                if (string.IsNullOrEmpty(this.NotificationTitle) || string.IsNullOrWhiteSpace(this.NotificationTitle))
                {
                    yield return new ValidationResult(
                        $"Notification Title is required",
                        new[] { nameof(this.NotificationTitle) });
                }
            }

1

查看ExpressiveAnnotations .net库 Git参考

它具有“ RequiredIf”和“ AssertThat”验证属性


-3

我不能完全给您您要的东西,但是您是否考虑过以下内容?

public abstract class Document // or interface, whichever is appropriate for you
{
    //some non-validted common properties
}

public class ValidatedDocument : Document
{
    [Required]
    public string Name {get;set;}
}

public class AnotherValidatedDocument : Document
{
    [Required]
    public string Name {get;set;}

    //I would suggest finding a descriptive name for this instead of Name2, 
    //Name2 doesn't make it clear what it's for
    public string Name2 {get;set;}
}

public class NonValidatedDocument : Document
{
    public string Name {get;set;}
}

//Etc...

理由是int DocumentType变量。您可以对需要处理的每个“类型”的文档使用具体的子类类型来代替它。这样做使您可以更好地控制属性注释。

看来在不同情况下只需要一些属性,这可能表明您的文档类正在做太多工作,并支持上述建议。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.