15

問題

MVC 内でモデルの検証を行う方法はたくさんあり、このトピックに関するドキュメントもたくさんあります。ただし、同じタイプの「サブモデル」であるモデルのプロパティを検証するための最良のアプローチが何であるかはよくわかりません。

次のことに注意してください

  • TryUpdateModel/TryValidateModel私はまだメソッドの利益を得たいです
  • これらの「サブモデル」にはそれぞれ、厳密に型指定されたビューがあります
  • MainModel全体的な表示ビューをレンダリングする、クラスの厳密に型指定されたビューが 1 つあります。

少し混乱するかもしれませんが、明確にするためにいくつかのコードを挿入します。例として、次のクラスを取り上げます。

メインモデル:

class MainModel{
    public SomeSubModel Prop1 { get; set; }
    public SomeSubModel Prop2 { get; set; }
}

一部のサブモデル:

class SomeSubModel{
      public string Name { get; set; }
      public string Foo { get; set; }
      public int Number { get; set; }
}

メインモデルコントローラー:

class MainModelController{

    public ActionResult MainDisplay(){
         var main = db.retrieveMainModel();
         return View(main); 
    }

    [HttpGet]
    public ActionResult EditProp1(){
         //hypothetical retrieve method to get MainModel from somewhere
         var main = db.retrieveMainModel();

         //return "submodel" to the strictly typed edit view for Prop1
         return View(main.Prop1);
    }

    [HttpPost]
    public ActionResult EditProp1(SomeSubModel model){

         if(TryValidateModel(model)){
              //hypothetical retrieve method to get MainModel from somewhere
              var main = db.retrieveMainModel();
              main.Prop1 = model;
              db.Save();

              //when succesfully saved return to main display page 
              return RedirectToAction("MainDisplay");
         }
         return View(main.Prop1);
    }

    //[...] similar thing for Prop2 
    //Prop1 and Prop2 could perhaps share same view as its strongly 
    //typed to the same class
}

このコードは今まですべて意味があると信じています (そうでない場合は修正してくださいTryValidateModel()) ValidationAttribute

問題はここにあります。最適な場所はどこか、また、Edit メソッドを条件付きステートメントで埋めずに利用しながら、 andにさまざまな検証制約を設定するための最良かつ最も洗練された方法はどこにあるでしょうか。Prop1Prop2TryValidateModel()ModelState.AddModelError()

通常、クラスに検証属性を含めることができますがSomeSubModel、この場合は機能しません。プロパティごとに異なる制約があるためです。

他のオプションは、クラスにカスタム検証属性が存在する可能性があることですが、オブジェクトがビューに直接渡され、検証時にそのオブジェクトへの参照がないMainModelため、この場合も機能しません。SomeSubModelMainModel

私が考えることができる唯一の残されたオプションは、各プロパティの ValidationModel ですが、これに対する最善のアプローチが何であるかはよくわかりません。

解決

@MrMindorの回答に基づいて、私が実装したソリューションを次に示します。

基本 ValidationModel クラス:

public class ValidationModel<T> where T : new()
{
    protected ValidationModel() {
        this.Model = new T();
    }
    protected ValidationModel(T obj) { 
        this.Model = obj; 
    }

    public T Model { get; set; }
}

Prop1 の検証モデル

public class Prop1ValidationModel:ValidationModel<SomeSubModel>
{
    [StringLength(15)]
    public string Name { get{ return base.Model.Name; } set { base.Model.Name = value; } }

    public Prop1ValidationModel(SomeSubModel ssm)
        : base(ssm) { }
}

Prop2 の検証モデル

public class Prop2ValidationModel:ValidationModel<SomeSubModel>
{
    [StringLength(70)]
    public string Name { get{ return base.Model.Name; } set { base.Model.Name = value; } }

    public Prop2ValidationModel(SomeSubModel ssm)
        : base(ssm) { }
}

アクション

[HttpPost]
public ActionResult EditProp1(SomeSubModel model){

     Prop1ValidationModel vModel = new Prop1ValidationModel(model);
     if(TryValidateModel(vModel)){

          //[...] persist data

          //when succesfully saved return to main display page 
          return RedirectToAction("MainDisplay");
     }
     return View(model);
}
4

2 に答える 2

4

それぞれがジョブのパラメーター設定を表すアプリケーションの 1 つに、同様の状況があります。SomeSubModelジョブの種類ごとにパラメーターの数と種類が異なるため、ジョブ モデルにはプロパティを設定するだけでなく、これらのパラメーターのコレクションがあります。

利用可能なさまざまなタイプ ( 、、、...)JobParameterにサブクラス化された があります。これらのサブクラスには、独自の検証属性のセットがあります。 パラメータをビューに渡すために、共有の「JobParameterModel」が使用されます。 検証のために、返されたモデルは特定の JobParameter に変換されます。パラメータの種類:StringParameterBoolParameterDoubleParameter


public enum ParameterType
{
    Empty = 0,
    Boolean = 1,
    Integer = 2,
    String = 3,
    DateTime = 4,
    ...
}

ジョブパラメータ:

class JobParameter
{ 
  [AValidationAttributeForAllParamters] 
  public string Name { get; set; }  
  public virtual string Foo { get; set; }  
  public int Number { get; set; }
  public ParameterType Type {get;set;}

  private static readonly IDictionary<ParameterType, Func<object>> ParameterTypeDictionary =
  new Dictionary<ParameterType, Func<object>>{
                {ParameterType.Empty, () => new EmptyParameter() },
                {ParameterType.String, ()=>new StringParameter()},
                {ParameterType.Password, ()=>new PasswordParameter()},
                ...
              };
    public static ScriptParameter Factory(ParameterType type)
    {
        return (ScriptParameter)ParameterTypeDictionary[type]();
    }
}  

ブールパラメータ:

[ABoolClassLevelValidationAttribute]
class BoolParameter:JobParameter
{
    [AValidationAttribute]
    public override string Foo {get;set;}
}

....

私たちの検証フレームワーク (MS のものに非常によく似ていると言われています) では、ViewModel は検証のために常にそのドメイン オブジェクトに変換されます。
パラメータモデル:

class ParameterModel: JobParameter
{
    public JobParameter ToDomain()
    {
        var domainObject = JobParameter.Factory(Type);
        Mapper.Map(this, domainObject);
        return domainObject;
    }
    public bool Validate()
    {
        var dom = ToDomain();
        return TryValidate(dom);
    }

}

コントローラ:

class Controller(){

    [HttpPost]                                
    public ActionResult SaveParameter(JobParameter model){                                

         if(TryValidateModel(model)){                                

              //persist stuff to db.

          //when succesfully saved return to main display page                                 
              return RedirectToAction("MainDisplay");                                
         }                                
         return View(main.Prop1);
    }                                
}                                

特定の状況のた​​めに、これほど複雑にする必要はありません (または、検証フレームワークの詳細が機能することを信頼してください)。
各プロップの編集/保存アクション: 各プロップ
の検証モデルを作成します。Prop1ValidationModelProp2ValidationModel

[HttpGet]
public ActionResult EditProp1()
{
    var main = db.retrieveMainModel();
    db.Prop1.SubmitUrl = Url.Action("SaveProp1","Controller");
    return View(main.Prop1);
}
[HttpPost]                                
public ActionResult SaveProp1(SomeSubModel model){                                
     var validationModel = new Prop1ValidationModel{
     ///copy properties                                   
         };
     if(TryValidateModel(validationModel)){                                

          var main = db.retrieveMainModel();                                
          main.Prop1 = model;                                
          db.Save();                                

          //when succesfully saved return to main display page                                 
          return RedirectToAction("MainDisplay");                                
     }                                
     return View(main.Prop1);
} 

これにより、Prop1 と Prop2 の両方に同じ厳密に型指定されたビューを使用できます。

于 2012-10-22T17:45:23.517 に答える
2

SomeSubModel が Prop1 または Prop2 のどちらに適用されるかに応じて異なる検証属性を持つ場合...実際には、prop1 と prop2 の 2 つの SomeSubModel は 2 つの異なるクラスであることを意味します。同じフィールドがある場合、このフィールドの意味は依存することによって異なるからです。 prop1 または prop2 にアタッチされている場合 (そのため、検証属性が異なります。したがって、最善の方法は、SomeSubClass の 2 つのサブクラスを定義することです。たとえば、Common SomeSubClass から継承する SomeSubClass1 と SomeSubClass2 を定義します。継承したら、新しいプロパティを追加するのではなく、流暢な検証を使用するか、MetaDataTypeAttributeを使用してクラス定義から Validation 属性を指定することにより、新しい検証規則を作成します。

[MetaDataType(typeof(ValidationClass1)]
public class SomeSubClass1: SomeSubclass{}

[MetaDataType(typeof(ValidationClass2)]
public class SomeSubClass2: SomeSubclass{}
于 2012-10-24T20:04:07.280 に答える