0

MVC 2 NerdDinner チュートリアルを進めていると、ビジネス ロジック (必須フィールド、最大長など) をモデルの個々のプロパティに追加できることがわかりました。

より複雑な検証ルールを追加するにはどうすればよいですか? たとえば、2 つのプロパティのうち 1 つだけを入力する必要がありますか?

例 [QuantumMechanics/Models/Particle.cs]:

namespace QuantumMechanics.Models
{
   [MetadataType(typeof(Particle_Validation))]
   public partial class Particle {
   }
   public class Particle_Validation
   {
       // Mass is required; easy enough.
       [Required(ErrorMessage="Mass is required.")]
       public Mass double {get; set; }

       // How do I require exactly one or the other?
       public Position double {get; set; }
       public Momentum double {get; set; }
   }
}
4

3 に答える 3

0

MVC2では、値がコントローラに送信された後にチェックを実行します。

[HttpPost]
public ActionResult Add(Particle particleToAdd)
{
    ValidateModel(particleToAdd);

    if(ModelState.IsValid)
    {
        // Add particle
        return RedirectToAction(...) // etc
    }

    // Return the view with our errors
    return View(particleToAdd);
}

// This validate method can be invoked from your Add and Edit actions
private void ValidateModel(Particle particleToAddOrUpdate)
{
    if(particleToAddOrUpdate.Position == null && particleToAddOrUpdate.Momentum == null)
    {
        ModelState.AddModelError("", "You must supply a value for either Position or Momentum");
    }
}

必要に応じてプロパティの 1 つ (ポジションまたはモメンタム) にエラーを追加できますが、検証の概要に表示される一般的なエラー リストに追加しただけです。

<%: Html.ValidationSummary() %>

MVC3には、このIValidateObject問題を少し抽象化するインターフェースがあります。Addクラスごとに指定したようにチェックを実行できます。つまり、パーティクル オブジェクトはバインディング時に独自のプロパティをチェックでき、メソッド内にチェックを記述する必要はありません。

その使用例を次に示します。

残念ながら、MVC2には、 MVC3IValidateObjectのインターフェイスのように、バインド時にクラスの複数のプロパティを検証できる、すぐに使用できるものはありません。またはアクションが呼び出されたときに、追加の Validate メソッドを呼び出すことを確認する必要があります。AddEdit

于 2012-04-20T20:08:58.773 に答える
0

あなたが目指しているのは、「条件付きで必要な」ルール、つまり、別のプロパティの値に基づいて必須としてマークされているプロパティのようです。これは、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です...

于 2012-04-23T14:43:42.993 に答える
-1

私が行った解決策を投稿すると思いました。Spencerooni が言ったように、モデル自体に複雑な検証を追加するエレガントなメカニズムはありませんが、これはうまくいくようです。これは、DefaultModelBinder インターフェイスの OnModelUpdated メソッド オーバーライドを利用して、基になるモデルが更新されるたびに無効にします。

モデルのプロパティの DataAnnotations は、バインドされたフィールドがポストバックされたときにのみ呼び出されることに注意してください。つまり、Mass のフィールドがまったくないフォームで検証が渡されます。

Global.asax

ModelBinders.Binders[typeof(Particle)] = new ParticleModelBinder();

モデル/ParticleModelBinder.cs

public class ParticleModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        base.OnModelUpdated(controllerContext, bindingContext);

        var particle = (Particle)bindingContext.Model;

        if (<Test for business rule violation here>)
        {
            var modelState = bindingContext.ModelState;
            // Message to appear in validation summary.
            modelState.AddModelError("","Please enter position OR momentum.");
            // Messages also appear in summary, but highlight the bound controls too.
            modelState.AddModelError(bindingContext.ModelName + ".Position",
                "Please enter position (or momemtum).");
            modelState.AddModelError(bindingContext.ModelName + ".Momentum",
                "Please enter momentum (or position).");
        }
    }
}
于 2012-04-23T13:22:41.013 に答える