23

私は大きなモデルを持っています(大きなとは、モデルクラスに多くのフィールド/プロパティが含まれ、それぞれに少なくとも1つの検証属性(RequiredMaxLengthなどMinLength)があることを意味します)。ユーザーがモデルにデータを入力するための多くの入力を含む1つのビューを作成する代わりに、ユーザーがモデルフィールドの一部を入力して次のステップ(ある種の「ウィザード」)に進む複数のビューを作成したいと思います。ステップ間でリダイレクトしている間、塗りつぶされていないモデルオブジェクトをに格納しますSession。以下のようなもの:

モデル:

public class ModelClass
{
    [MaxLength(100)] ...
    public string Prop1{get;set;}
    [MaxLength(100)] ...
    public string Prop2{get;set;}
    ...
    [Required][MaxLength(100)] ...
    public string Prop20{get;set;}
}

コントローラ:

[HttpPost]
public ActionResult Step1(ModelClass postedModel)
{    
    // user posts only for example Prop1 and Prop2
    // so while submit I have completly emty model object
    // but with filled Prop1 and Prop2
    // I pass those two values to Session["model"]
    var originalModel = Session["model"] as ModelClass ?? new ModelClass();
    originalModel.Prop1 = postedModel.Prop1;
    originalModel.Prop2 = postedModel.Prop2;
    Session["model"] = originalModel;

    // and return next step view
    return View("Step2");
}

[HttpPost]
public ActionResult Step2(ModelClass postedModel)
{
    // Analogically the same
    // I have posted only Prop3 and Prop4

    var originalModel = Session["model"] as ModelClass;
    if (originalModel!=null)
    {
        originalModel.Prop3 = postedModel.Prop3;
        originalModel.Prop4 = postedModel.Prop4;
        Session["model"] = originalModel;

        // return next step view
        return View("Step3");
    }
    return View("SomeErrorViewIfSessionBrokesSomeHow")
}

Step1ビューにはとの入力のみがProp1ありProp2、ステップ2ビューにはProp3との入力が含まれていますProp4

しかし、ここにあるものがあります

たとえば、ユーザーがオンの場合、手順1で、Prop1に100文字を超える値を入力すると、クライアント側の検証が正常に機能します。しかし、もちろん、私はこの値を検証し、コントローラーのサーバー側で検証する必要があります。フルモデルがあれば、次のようにします。

if(!ModelState.IsValid) return View("the same view with the same model object");

そのため、ユーザーはフォームにもう一度入力して修正する必要があります。 しかし、ステップ1で、ユーザーは20の2つのプロパティのみを入力したため、それらを検証する必要があります。ModelState.IsValidモデルの状態が無効になるため使用できません。ご覧のとおり、ユーザーが送信して、がnullの場合、属性でProp20マークされているため、は無効です。もちろん、ユーザーがステップ2に進み、すべてのステップを入力し、最後のステップでのみモデルの状態を検証できるようにすることはできますが、ユーザーがステップ1を間違って入力した場合、ユーザーがステップ2に進むことを許可したくありません。そして、私はコントローラーでこの検証が必要です。したがって、問題は次のとおりです。モデルの一部のみを検証するにはどうすればよいですか?一部のモデルプロパティのみが検証属性と一致することを確認するにはどうすればよいですか?[Required]Prop1Prop2Prop20ModelState

4

4 に答える 4

16

考えられる解決策の1つ:

  1. ModelState.IsValidField(string key);を使用します。

    if (ModelState.IsValidField("Name") && ModelState.IsValidField("Address"))
    { ... }
    

そして、すべてが完了したら、最後に次を使用します。

if(ModelState.IsValid) { .. }
于 2013-01-11T15:32:55.063 に答える
11

最もエレガントな方法は、次のようにすることだと思います。

List<string> PropertyNames = new List<string>()
{
    "Prop1",
    "Prop2"
};

if (PropertyNames.Any(p => !ModelState.IsValidField(p)))
{
    // Error
}
else
{
    // Everything is okay
}

また:

List<string> PropertyNames = new List<string>()
{
    "Prop1",
    "Prop2"
};

if (PropertyNames.All(p => ModelState.IsValidField(p)))
{
    // Everything is okay
}
else
{
    // Error
}
于 2013-01-11T15:44:47.273 に答える
6

MVCコアでは、これは次と同等になります。

if (ModelState.GetFieldValidationState("Name") == Microsoft.AspNetCore.Mvc.ModelBinding.ModelValidationState.Valid)
{
    // do something
}

ただし、この場合は、単に別のビューモデルを作成することをお勧めします。

部分ビューモデルは、より大きなビューモデルに継承される可能性があるため、コード(DRYプリンシパル)で繰り返す必要はありません。

プロパティ名をハードコーディングすることは避けたほうがよいでしょう。

于 2018-01-08T14:40:19.180 に答える
5

これに対する既存の答えに追加するだけです。プロパティ名をハードコーディングするのではなく、次の行に沿って残りの検証属性とともに追加する属性を使用します。

public class ValidationStageAttribute : Attribute
{
    public int StageNumber { get; private set; }
    public ValidationStageAttribute(int stageNumber)
    {
        StageNumber = stageNumber;
    }
}

モデル自体の知識がなくてもプロパティ名を取得できるようになったので、部分的な検証をメソッドに取り込むことができます(これを頻繁に使用する場合は、ベースコントローラーが適しています)。

protected bool ValidateStage(object viewModel, int stageToValidate)
{
    var propertiesForStage = viewModel.GetType()
        .GetProperties()
        .Where(prop => prop.GetCustomAttributes(false).OfType<ValidationStageAttribute>().Any(attr => attr.StageNumber == stageToValidate))
        .Select(prop => prop.Name);

    return propertiesForStage.All(p => ModelState.IsValidField(p));
}

これで、投稿アクションで行う必要があるのは、電話することだけです。ValidateStage(viewModel, 1)

于 2016-07-20T13:57:34.337 に答える