5

次のような ViewModel クラスがあります。

class CaseModel {
    public Boolean     ClientPresent { get; set; }
    public ClientModel Client        { get; set; }
}

class ClientModel {
    [Required]
    public String      FirstName     { get; set; }
    [Required]
    public String      LastName      { get; set; }
}

ビュー ページは、<input type="checkbox" name="ClientPresent" />Html.EditorFor( m => m.Client )部分ビューで構成されます。

ユーザーがケース (ビジネス ドメイン オブジェクト) に関する情報を提供する場合、ClientPresent ボックスのチェックを外すことで、クライアント (別のビジネス オブジェクト) に関する情報を指定しないことを選択できるという考えです。

ASP.NET MVC で子 ClientModel オブジェクトの検証を実行しないようにしたい - ただし、フォームがサーバーに POST されたときに CaseModel.Client プロパティが自動的に設定されますが、(必ずしも) ユーザーによって提供されないためFirstNameですLastName[Required]検証属性に失敗したことを意味し、結果としてViewData.ModelState.IsValidfalse を返し、ユーザーは検証エラー メッセージを受け取ります。

false のCaseModel.Client場合は検証されないようにするにはどうすればよいですか?CaseModel.ClientPresent

ClientModelは完全に独立した ViewModel クラスであり、アプリケーションの他の場所 (ユーザーがクライアントの個々のインスタンスを編集できるようにする ClientController クラスなど) で使用されることに注意してください。

4

2 に答える 2

3

私の問題はバインディングではなく、実際には検証に関係していることを認識しています。値を保持することは、ユーザーがページをリロードしたときに同じフォームフィールドにデータが入力されることを意味します。該当する。

そのために、モデル プロパティの検証を実行できることに気付きましたが、カスタム ロジックを使用して検証メッセージを削除しました。これは私がやったことと似ています:

public class CaseModel {
    public void CleanValidation(ModelStateDictionary dict) {
        if( this.ClientPresent ) {
            dict.Keys.All( k => if( k.StartsWith("Client") dict[k].Errors.Clear() );
        }
    }
}

(明らかに、私の実際のコードはより堅牢ですが、一般的な考え方は理解できます)

CleanValidation メソッドは、コントローラーのアクション メソッドによって直接呼び出されます。

public void Edit(Int64 id, CaseModel model) {
    model.CleanValidation( this.ModelState );
}

CleanValidation新しいインターフェイスにメソッドとして追加し、新しいモデル バインダーがこのメソッドを自動的に呼び出すようにすることで、おそらくこれを整理できるIComplexModelので、コントローラーがそれ自体を呼び出す必要はありません。

アップデート:

複雑な検証が必要な ViewModel に適用されるこのインターフェイスがあります。

public interface ICustomValidation {

    void Validate(ModelStateDictionary dict);
}

私の元の例では、CaseModel次のようになります。

 public class CaseClientModel : ICustomValidation {

      public Boolean ClientIsNew { get; set; } // bound to a radio-button
      public ClientModel ExistingClient { get; set; } // a complex viewmodel used by a partial view
      public ClientModel NewClient { get; set; } // ditto

      public void Validate(ModelStateDictionary dict) {

          // RemoveElementsWithPrefix is an extension method that removes all key/value pairs from a dictionary if the key has the specified prefix.
          if( this.ClientIsNew ) dict.RemoveElementsWithPrefix("ExistingClient");
          else                   dict.RemoveElementsWithPrefix("NewClient");
      }
 }

検証ロジックはOnActionExecuting、私の共通BaseControllerクラスで呼び出されます。

protected override void OnActionExecuting(ActionExecutingContext filterContext) {
    base.OnActionExecuting(filterContext);
    if( filterContext.ActionParameters.ContainsKey("model") ) {

        Object                    model = filterContext.ActionParameters["model"];
        ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState; // ViewData.Model always returns null at this point, so do this to get the ModelState.

        ICustomValidation modelValidation = model as ICustomValidation;
        if( modelValidation != null ) {
            modelValidation.Validate( modelState );
        }
    }
}
于 2012-06-14T14:24:14.860 に答える
2

既定のモデル バインダーから継承して、カスタム モデル バインダーを作成する必要があります。

  public class CustomModelBinder: DefaultModelBinder
  {
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
    {
      if (propertyDescriptor.Name == "Client")
      {
          var clientPresent = bindingContext.ValueProvider.GetValue("ClientPresent");

          if (clientPresent == null || 
                string.IsNullOrEmpty(clientPresent.AttemptedValue))
              return;
      }

      base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
    }
  }

Global.asax.cs

ModelBinders.Binders.Add(typeof(CaseModel), new CustomModelBinder());
于 2012-06-14T02:56:16.787 に答える