web-dev-qa-db-ja.com

属性を動的にアタッチするDataAnnotations

明らかに、実行時にDataAnnotation属性をオブジェクトプロパティに動的にアタッチして、動的検証を実現することが可能です。

誰かがこれに関するコードサンプルを提供できますか?

31
mare

MVCには、独自のModelValidatorProviderを提供するためのフックがあります。デフォルトでは、MVC 2は、検証にSystem.DataAnnotations.ComponentModel.ValidationAttribute属性を使用できるDataAnnotationsModelValidatorProviderと呼ばれるModelValidatorProviderのサブクラスを使用します。

DataAnnotationsModelValidatorProviderは、リフレクションを使用してすべてのValidationAttributesを検索し、コレクションをループしてモデルを検証します。 GetValidatorsというメソッドをオーバーライドし、選択したソースから独自の属性を挿入するだけです。私はこの手法を使用して規則の検証を行い、DataType.Email属性を持つプロパティは常に正規表現を介して渡され、この手法を使用してデータベースから情報を取得し、「非パワー」ユーザーに対してより制限的な検証を適用します。

次の例では、「常にFirstNameプロパティを必須にする」と単純に述べています。

 public class CustomMetadataValidationProvider : DataAnnotationsModelValidatorProvider
 {
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        //go to db if you want
        //var repository = ((MyBaseController) context.Controller).RepositorySomething;

        //find user if you need it
        var user = context.HttpContext.User;

        if (!string.IsNullOrWhiteSpace(metadata.PropertyName) && metadata.PropertyName == "FirstName")
            attributes = new List<Attribute>() {new RequiredAttribute()};

        return base.GetValidators(metadata, context, attributes);
    }
}

あなたがしなければならないのはあなたのGlobal.asax.csファイルにプロバイダーを登録することです:

    protected void Application_Start()
    {
        ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());

        AreaRegistration.RegisterAllAreas();

        RegisterRoutes(RouteTable.Routes);
    }

最終結果:

end result

このモデルで:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthday { get; set; }
}
42
John Farrell

Global.asaxで、新しいモデルを追加する前にModelValidatorProvidersをクリアする必要があります。それ以外の場合は、すべての注釈が2回追加され、「目立たないクライアント検証ルールの検証タイプ名は一意である必要があります。」-エラーが発生します。

protected void Application_Start()
{
    ModelValidatorProviders.Providers.Clear();
    ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());

    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);
}
8
Frank Horemans

オーバーライドされたMetadataValidationProviderでカスタムGetValidatorsを使用するアプローチには、いくつかの弱点があります。

  • DisplayAttributeなどの一部の属性は検証に関連していないため、検証段階でそれらを追加しても機能しません。
  • 将来を見据えたものではないかもしれません。フレームワークを更新すると、動作が停止する可能性があります。

動的に適用されるデータ注釈を一貫して機能させたい場合は、DataAnnotationsModelMetadataProviderDataAnnotationsModelValidatorProviderをサブクラス化できます。これを行った後、replaceアプリケーションの起動時にModelMetadataProviders.CurrentおよびModelValidatorProviders.Providersを介してフレームワークのものを置き換えます。 (Application_Startでそれを行うことができます。)

組み込みプロバイダーをサブクラス化する場合、独自の属性を適用するための体系的で将来性のある方法は、GetTypeDescriptorをオーバーライドすることです。私はこれを成功させましたが、ICustomTypeDescriptorPropertyDescriptorの実装を作成する必要があり、多くのコードと時間が必要でした。

0
Sam