ASP.NET MVC TryValidateModel() Issues when Model is Modified

SimonGates picture SimonGates · Dec 20, 2011 · Viewed 18k times · Source

I have a two step form process where the first set of data is stored in session.

[IsMp4File]
[Required(ErrorMessage = "* Please select a video to upload")]
public HttpPostedFileBase VideoClip { get; set; }

[Required(ErrorMessage = "* Please select a thumbmail image")]
public HttpPostedFileBase VideoThumbnail{ get;  set; }

public string VideoFileName { get { return VideoClip.FileName; } }

public NewsWizardStep CurrentStep { get; set; }

...

public enum NewsWizardStep : int
{
  One = 1,
  Two = 2,
  Three = 3,
  Four = 4,
  Five = 5,
  Six = 6
}

Controller

public ActionResult TvCreate(TvNewsVideoVM modelVM)
{
   if (modelVM.CurrentStep == NewsWizardStep.Two)
   {
     var sessionModel = ((TvNewsVideoVM)Session["TvModelVM"]);

     modelVM.VideoClip = sessionModel.VideoClip;
     modelVM.VideoThumbnail = sessionModel.VideoThumbnail;
   }

   if (TryValidateModel(modelVM))
   {
     ...
   }
}

TryValidateModel(modelVM) returns false, saying VideoClip and VideoThumnail are required, despite mapping them from the seesionModel to the viewModel. I have added a breakpoint and checked they are not null.

It looks like there is some underlying functionality I am not aware of regarding how ModelState and ValidateModel() work , I just don't know what.

UPDATE

I wouldn't say I have resolved the issue but figured out a workaround that isn't that pretty, By going into the ModelState it is possible to set the ModelValue using SetModelValue() then manually remove the error from the model state and then call TryValidateModel() - you might not even have to add the values just remove the error I have not tried. Here is my work around.

if (modelVM.CurrentStep == NewsWizardStep.Two)
{
  var sessionModel = ((MtTvNewsVideoVM)Session["MtTvModelVM"]);

  modelVM.VideoClip = sessionModel.VideoClip;
  modelVM.VideoThumbnail = sessionModel.VideoThumbnail;

  ModelState.SetModelValue("VideoClip", new  ValueProviderResult(sessionModel.VideoThumbnail, sessionModel.VideoFileName, CultureInfo.CurrentCulture));
                ModelState.SetModelValue("VideoThumbnail", new ValueProviderResult(sessionModel.VideoClip, sessionModel.VideoFileName, CultureInfo.CurrentCulture));

  ModelState["VideoClip"].Errors.RemoveAt(0);
  ModelState["VideoThumbnail"].Errors.RemoveAt(0);
}

Answer

nemesv picture nemesv · Dec 20, 2011

During the model binding the DefaultModelBinder validates your action parameters. So when the execution hits your public ActionResult TvCreate(TvNewsVideoVM modelVM) method the ModelState is already containing the validation errors.
When you call TryValidateModel it doesn't clear the ModelState so the validation errors remain there that is why it returns false. So you need to clear the ModelState collection if you want to redo the validation later manually:

public ActionResult TvCreate(TvNewsVideoVM modelVM)
{
   ModelState.Clear();

   if (modelVM.CurrentStep == NewsWizardStep.Two)
   {
     var sessionModel = ((TvNewsVideoVM)Session["TvModelVM"]);

     modelVM.VideoClip = sessionModel.VideoClip;
     modelVM.VideoThumbnail = sessionModel.VideoThumbnail;
   }

   if (TryValidateModel(modelVM))
   {
     ...
   }
}