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

1

18

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);
}
Bern answered 20/12, 2011 at 11:11 Comment(0)
S
33

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))
   {
     ...
   }
}
Steeplechase answered 20/12, 2011 at 21:1 Comment(5)
Thats better thanks - I don't understand why the the TryValidateModel() method doesn't call the Clear() Method first and then attempt to validate the Model.Bern
if you're only interested in doing manual validation, you can skip the initial validation magic by decorating the Controller method with <ValidateInput(False)>Burgas
That ValidateInput false is the key here. It'll keep the Values in ModelState but just skip the auto check thing. The ModelState.Clear is good but it'll clear those values out.Cordle
Wait, hang on that doesn't seem to be the case - not sure what ValidateInput does to the ModelStateCordle
This saved me a bit of searching, was not even thinking about this being set before it reaches my methods.Everett

© 2022 - 2024 — McMap. All rights reserved.