Question

I am working on a project written on mvc 4 that got several instances of wizard-like behaviour - chain of few views that pass the same half-filled model. Starting from the second view controls are initially shown as non-valid (which is logical - model, passed to controller method has corresponding properties empty). Currently ModelState.Clear(); solution is used, but putting it into each method with model as an argument looks ugly. Same with approach found here Disable Model Validation in Asp.Net MVC

ModelBinders.Binders[typeof(MyModelType)] = new NonValidatingModelBinder();

Project got too many (over 100) model classes to register each one manually.

Is there simpler way (maybe key in .config) that turns off model validation completely?

Was it helpful?

Solution

I don't know it this could work but did you try this (bootstrap code or global.asax)

ModelValidatorProviders.Providers.Clear();

OTHER TIPS

I know it doesn't really answer your question, but just to expand on my comment:-

It sounds like you have something like:-

public class MyModel
{
  [Required]
  public string Foo { get; set; } // Populated in step 1
  [Required]
  public string Bar { get; set; } // Populated in step 2
}

You have a problem when you POST step 1 because the user hasn't entered a value for Bar yet, so there's a ModelStateError.

My preferred solution, rather than trying to mess around with validation of your persistence model, would be to decouple your View implementation from your Model implementation with a ViewModel for each Wizard step, for example:-

public class MyModel
{
  [Required]
  public string Foo { get; set; }
  [Required]
  public string Bar { get; set; }
}

public class StepOneModel
{
  [Required]
  public string Foo { get; set; }
}

public class StepTwoModel
{
  // This really depends on the behaviour you want.
  // In this example, the model isn't persisted until
  // the last step, but you could equally well persist
  // the partial model server-side and just include a
  // key in subsequent wizard steps.
  [Required]
  public StepOneModel StepOne { get; set; }

  [Required]
  public string Bar { get; set; }
}

Your controller actions look something like:-

public ActionResult StepOne()
{
  return View(new StepOneViewModel());
}
[HttpPost]
public ActionResult StepOne(StepOneViewModel model)
{
  if(ModelState.IsValid)
  {
    var stepTwoModel = new StepTwoViewModel ()
    {
      StepOne = model
    };

    // Again, there's a bunch of different ways
    // you can handle flow between steps, just
    // doing it simply here to give an example
    return View("StepTwo", model);
  }

  return View(model);
}
[HttpPost]
public ActionResult StepTwo(StepTwoViewModel model)
{
  if (ModelState.IsValid)
  {
    // You could also add a method to the final ViewModel
    // to do this mapping, or use something like AutoMapper
    MyModel model = new MyModel()
    {
      Foo = model.StepOne.Foo
      Bar = model.Bar
    };

    this.Context.MyModels.Add(model);
    this.Context.SaveChanges();
  }

  return View(model);
}

Your StepOne view looks something like:-

@model StepOneModel
@using (html.BeginForm()) {
   @html.EditorFor(x => x.Foo);
}

Your StepTwo view looks something like:-

@model StepTwoModel
@using (html.BeginForm("StepTwo")) {
  @html.HiddenFor(x => x.StepOne);
  @html.EditorFor(x => x.Bar);
}

The major advantage compared to just turning off model validation is that you can put the validation requirements for the current step on your ViewModel - you can ensure that all the values from step one are valid before proceeding to step two.

  • If you want a more RESTful approach (where your controller doesn't care that the data is coming from a wizard-style view) another popular solution is to wrap each step in a <div> client-side and use javascript to hide/show them as the user progresses.

  • If your model needs to be persisted between steps, then you'll need to think about the ValidationAttributes on your model, and what they mean. If you've annotated User.DateOfBirth with Required, but you need to be able to persist it before the step it gets populated, then in fact User.DateOfBirth isn't required (e.g. EF CodeFirst can't make the column NOT NULL because we need to be able to persist null values in the meantime). You'll need to do some conditional validation (e.g. IValidatableObject, MvcFoolproof, Fluent Validation) in order to validate your complete model later.

I don't know about all an option in web.config or something like that. But you could use this:

Assembly.GetExecutingAssembly()
   .GetTypes()
   .Where(t => t.IsClass && t.Namespace == "Your.Name.Space")
   .ToList()
   .ForEach(t => ModelBinders.Binders[t] = new NonValidatingModelBinder());

Or

typeof(MyModelType)
   .Assembly
   .GetTypes()
   .Where(t => t.IsClass && t.Namespace == "Your.Name.Space")
   .ToList()
   .ForEach(t => ModelBinders.Binders[t] = new NonValidatingModelBinder());

Or Remove the && t.Namespace == "Your.Name.Space" part so it will add all the classes in your assembly to NonValidatingModelBinder.

Don't forget to add using System.Linq;

@{
HtmlHelper.ClientValidationEnabled = false;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top