Условно-требуемое свойство с использованием аннотаций данных
У меня есть класс вроде этого:
public class Document
{
public int DocumentType{get;set;}
[Required]
public string Name{get;set;}
[Required]
public string Name2{get;set;}
}
Теперь, если я помещаю аннотацию данных [Required]
в свойствах Name
и Name2
, тогда все в порядке, и если Name
или Name2
пустые, проверка приведет к ошибке.
Но я хочу, чтобы поле Name
требовалось только в том случае, если DocumentType
равно 1
и Name2
требуется только, если DocumentType
равно 2.
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;}
}
но я знаю, что не могу, это вызывает ошибку. Что я должен сделать для этого требования?
Ответы
Ответ 1
Из коробки я думаю, что это все еще невозможно.
Но я нашел эту перспективную статью о Mvc.ValidationToolkit (также здесь, к сожалению этот является только альфой, но вы, вероятно, могли бы также извлечь из этого кода нужные вам методы и интегрировать его по своему усмотрению), он содержит приятный звуковой атрибут RequiredIf
, который, по-видимому, соответствует именно вашей причине:
- вы загружаете проект из связанного почтового индекса и создаете его
- получить встроенную dll из вашей папки сборки и ссылаться на нее в проекте, который вы используете
- К сожалению, это, похоже, также требует ссылки на MVC (самый простой способ - запустить MVC-проект в VS или
install-package Microsoft.AspNet.Mvc
)
- в файлах, где вы хотите их использовать, добавьте
using Mvc.ValidationToolkit;
- тогда вы можете писать такие вещи, как
[RequiredIf("DocumentType", 2)]
или [RequiredIf("DocumentType", 1)]
, поэтому объекты действительны, если не указаны ни name
, либо name2
, если DocumentType
не равно 1 или 2
Ответ 2
RequiredIf атрибут проверки
Я написал 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 value should match or differ from provided other property value (default is <c>false</c>).
/// </summary>
/// <value>
/// <c>true</c> if other property 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;
}
}
Ответ 3
Условно-требуемое свойство с использованием аннотаций данных
[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));
}
}
}
Ответ 4
Отправляйте флажок Проверка работоспособности
https://www.nuget.org/packages/FluentValidation/
Описание проекта
Небольшая библиотека проверки подлинности для .NET, которая использует свободный интерфейс и лямбда-выражения для создания правил проверки для ваших бизнес-объектов.
https://github.com/JeremySkinner/FluentValidation
Ответ 5
Проверьте MVC Foolproof проверки. Он имеет аннотацию данных в модели, как RequiredIf (зависимое свойство, зависимое значение), если я правильно помню. Вы можете скачать Foolproof с:
Visual Studio (2017) → Инструменты → Диспетчер пакетов Nuget → Управление пакетами Nuget для решения. Ссылка на mvcfoolproof.unobtrusive.min.js в дополнение к файлам jquery.
Ответ 6
Я всегда использовал реализованный IValidatableObject из System.ComponentModel.DataAnnotations;
Пример ниже
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) });
}
}
Ответ 7
Я не могу дать вам именно то, о чем вы просите, но рассмотрели ли вы что-то вроде следующего?
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 for
public string Name2 {get;set;}
}
public class NonValidatedDocument : Document
{
public string Name {get;set;}
}
//Etc...
Обоснование - это переменная int DocumentType
. Вы можете заменить это на использование конкретных типов подклассов для каждого "типа" документа, с которым вам нужно иметь дело. Это дает вам гораздо лучший контроль над аннотациями вашего имущества.
Также кажется, что только некоторые из ваших свойств необходимы в разных ситуациях, что может быть признаком того, что ваш класс документов пытается сделать слишком много, и поддерживает это предложение выше.