あなたが目指しているのは、「条件付きで必要な」ルール、つまり、別のプロパティの値に基づいて必須としてマークされているプロパティのようです。これは、ValidationAttribute だけでは不可能です。その属性のスコープは、それが装飾するプロパティだけに限定されるためです。DataAnnotationsModelValidator
より広いスコープを持つように一致を実装する必要があります(ModelClientValidationRule
クライアント側も検証する場合は、関連付けられたクライアント側の JavaScript を使用します)。
したがって、ConditionalRequiredAttribute は次のようになります。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class ConditionalRequiredAttribute : ValidationAttribute
{
public ConditionalRequiredAttribute(string triggerProperty, object triggerValue)
{
TriggerProperty = triggerProperty;
TriggerValue = triggerValue;
}
/// <summary>
/// Checks that the value of the decorated member is not null/empty if the TriggerProperty's value is equal to TriggerValue.
/// </summary>
/// <param name="value">The data field value to validate.</param>
/// <returns>true if validation is successful; otherwise, false.</returns>
public override bool IsValid(object value)
{
if (value == null)
return false;
if (value is double)
{
return !((double)value).Equals(0);
}
string s = value as string;
if (s != null)
return s.Length > 0;
return true;
}
/// <summary>
/// The name of the property whose value will be checked to trigger the required field
/// </summary>
public string TriggerProperty { get; set; }
/// <summary>
/// The expected value of the trigger property that will trigger the required field
/// </summary>
public object TriggerValue { get; set; }
}
これは実際には標準属性とほぼ同じですRequired
。「特別なソース」は実際にはバリデーターでありIsValid
、特定の状況 (つまりTriggerProperty.Value
== TriggerValue
) でのみメソッドを呼び出すために使用します。Validator は次のようになります。
public class ConditionalRequiredValidator : DataAnnotationsModelValidator<ConditionalRequiredAttribute>
{
public ConditionalRequiredValidator(ModelMetadata metadata, ControllerContext context,
ConditionalRequiredAttribute attribute)
: base(metadata, context, attribute)
{
}
/// <summary>
/// Override the default validate method to only execute if the TriggerProperty's value is equal to TriggerValue
/// </summary>
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// Does the specified property exist in the metadata?
PropertyInfo triggerProperty = Metadata.ContainerType.GetProperty(Attribute.TriggerProperty);
if (triggerProperty != null)
{
object actualValue = triggerProperty.GetValue(container, null);
if (actualValue != null)
{
if (Attribute.TriggerValue.Equals(actualValue))
{
// Run IsValid for the property if the actual value matches the expected value
foreach (ModelValidationResult result in base.Validate(container))
{
yield return result;
}
}
}
}
}
}
最後に、 をConditionalRequiredValidator
プロバイダに登録して、ConditionalRequiredAttribute
が使用されたときにフレームワークがそれを優先するようにする必要があります。これを行うには、次の行Application_Start()
を Global.asax.cs のメソッドに追加します。
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ConditionalRequiredAttribute), typeof(ConditionalRequiredValidator));
次に、パーティクル クラス (または他のクラス) のメンバーを次のように装飾します。
public class Particle
{
// Mass is required; easy enough.
[Required(ErrorMessage="Mass is required.")]
public double Mass { get; set; }
[ConditionalRequired("Momentum", 0D, ErrorMessage = "Position must be set if Momentum is not.")]
public double Position { get; set; }
[ConditionalRequired("Position", 0D, ErrorMessage = "Momentum must be set if Position is not.")]
public double Momentum { get; set; }
}
これで、あるフィールドを別のフィールドの値に基づいて条件付きで検証できるようになりました。
ちなみに、この条件付きロジックをヘルパー クラスに抽象化し、一連の「条件付き」バリデータ、ConditionalRequired、ConditionalRange などを作成できます。
注2:おそらくあなた自身のソリューションよりも複雑/「より多くのコード」ですが(私がまだこの返信をまとめている間に投稿されました-doh!)、これには非常に再利用できるという利点があります. 将来のビューモデルに同じ機能を追加するには、プロパティを属性で装飾するだけConditionalRequired
です...