45

今では、優れたFluentValidation ライブラリを使用して、モデル クラスを検証しています。Web アプリケーションでは、jquery.validateプラグインと組み合わせて使用​​し、クライアント側の検証も実行します。欠点の 1 つは、検証ロジックの多くがクライアント側で繰り返され、1 か所に集中していないことです。

このため、私は代替手段を探しています。モデルの検証を実行するためのデータ注釈の使用を示す多くの例がありますそれは非常に有望に見えます。私が見つけられなかったのは、別のプロパティ値に依存するプロパティを検証する方法です。

たとえば、次のモデルを見てみましょう。

public class Event
{
    [Required]
    public DateTime? StartDate { get; set; }
    [Required]
    public DateTime? EndDate { get; set; }
}

EndDateが より大きいことを確認したいと思いStartDateます。カスタム検証ロジックを実行するために、ValidationAttributeを拡張するカスタム検証属性を作成できます。残念ながら、モデル インスタンスを取得する方法が見つかりませんでした。

public class CustomValidationAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        // value represents the property value on which this attribute is applied
        // but how to obtain the object instance to which this property belongs?
        return true;
    }
}

検証中のオブジェクト インスタンスを含むこのプロパティがあるため、 CustomValidationAttributeが機能しているように見えることがわかりました。ValidationContext残念ながら、この属性は .NET 4.0 でのみ追加されました。私の質問は、.NET 3.5 SP1 で同じ機能を実現できますか?


アップデート:

FluentValidationは、ASP.NET MVC 2 でクライアント側の検証とメタデータを既にサポートしているようです。

それでも、データ注釈を使用して依存プロパティを検証できるかどうかを知っておくとよいでしょう。

4

5 に答える 5

29

MVC2 には、DataAnnotations を機能させる方法を示すサンプルの "PropertiesMustMatchAttribute" が付属しており、.NET 3.5 と .NET 4.0 の両方で機能するはずです。そのサンプル コードは次のようになります。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
{
    private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";

    private readonly object _typeId = new object();

    public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
        : base(_defaultErrorMessage)
    {
        OriginalProperty = originalProperty;
        ConfirmProperty = confirmProperty;
    }

    public string ConfirmProperty
    {
        get;
        private set;
    }

    public string OriginalProperty
    {
        get;
        private set;
    }

    public override object TypeId
    {
        get
        {
            return _typeId;
        }
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
            OriginalProperty, ConfirmProperty);
    }

    public override bool IsValid(object value)
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
        object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
        object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
        return Object.Equals(originalValue, confirmValue);
    }
}

その属性を使用するときは、モデル クラスのプロパティに配置するのではなく、クラス自体に配置します。

[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public class ChangePasswordModel
{
    public string NewPassword { get; set; }
    public string ConfirmPassword { get; set; }
}

カスタム属性で「IsValid」が呼び出されると、モデル インスタンス全体が渡されるため、依存プロパティの値をそのまま取得できます。このパターンに従って、日付比較属性、またはより一般的な比較属性を簡単に作成できます。

Brad Wilson のブログに、クライアント側の検証部分を追加する方法を示す良い例がありますが、その例が .NET 3.5 と .NET 4.0 の両方で機能するかどうかはわかりません。

于 2010-03-02T00:16:44.800 に答える
15

私はこの非常に問題を抱えていて、最近私のソリューションをオープンソース化しました: http://foolproof.codeplex.com/

上記の例に対するフールプルーフのソリューションは次のようになります。

public class Event
{
    [Required]
    public DateTime? StartDate { get; set; }

    [Required]
    [GreaterThan("StartDate")]
    public DateTime? EndDate { get; set; }
}
于 2010-04-06T03:03:00.923 に答える
7

PropertiesMustMatch の代わりに、MVC3 で使用できる CompareAttribute を使用します。このリンクによるとhttp://devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-1 :

public class RegisterModel
{
    // skipped

    [Required]
    [ValidatePasswordLength]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }                       

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation do not match.")]
    public string ConfirmPassword { get; set; }
}

CompareAttributeは、実際には System.ComponentModel.DataAnnotations の一部ではありませんが、チームによって System.Web.Mvc DLL に追加された、非常に便利な新しいバリデーターです。特に適切な名前ではありませんが (唯一の比較は等しいかどうかをチェックすることなので、おそらく EqualTo の方が明白でしょう)、このバリデーターが 1 つのプロパティの値が別のプロパティの値と等しいことをチェックすることは使用方法から簡単にわかります。 . コードから、属性が、比較している他のプロパティの名前である文字列プロパティを受け取ることがわかります。このタイプのバリデーターの古典的な使用法は、ここで使用しているもの、つまりパスワードの確認です。

于 2011-05-23T04:28:55.323 に答える
3

.NET 3.5 の DataAnnotations のメソッドでは、検証された実際のオブジェクトまたは検証コンテキストを提供できないため、これを実現するにはちょっとした工夫が必要になります。私は ASP.NET MVC に詳しくないので、MCV と組み合わせてこれを行う方法を正確に言うことはできませんが、thread-static 値を使用して引数自体を渡すことはできます。これはうまくいくかもしれない何かの例です。

最初に、コール スタックを経由せずにオブジェクトを渡すことができる、ある種の「オブジェクト スコープ」を作成します。

public sealed class ContextScope : IDisposable 
{
    [ThreadStatic]
    private static object currentContext;

    public ContextScope(object context)
    {
        currentContext = context;
    }

    public static object CurrentContext
    {
        get { return context; }
    }

    public void Dispose()
    {
        currentContext = null;
    }
}

次に、ContextScope を使用するバリデーターを作成します。

public class CustomValidationAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
         Event e = (Event)ObjectContext.CurrentContext;

         // validate event here.
    }
}

そして最後に、オブジェクトが ContextScope を通過していることを確認します。

Event eventToValidate = [....];
using (var scope new ContextScope(eventToValidate))
{
    DataAnnotations.Validator.Validate(eventToValidate);
}

これは役に立ちますか?

于 2010-02-17T17:00:58.507 に答える